forked from mirrors/linux
		
	rtc: s3c: add s3c_rtc_data structure to use variant data instead of s3c_cpu_type
Add s3c_rtc_data structure to variant data according to SoC type. The s3c_rtc_data structure includes some functions to control RTC operation and specific data dependent on SoC type. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									d67288da51
								
							
						
					
					
						commit
						ae05c95074
					
				
					 1 changed files with 297 additions and 180 deletions
				
			
		|  | @ -32,17 +32,6 @@ | ||||||
| #include <asm/irq.h> | #include <asm/irq.h> | ||||||
| #include "rtc-s3c.h" | #include "rtc-s3c.h" | ||||||
| 
 | 
 | ||||||
| enum s3c_cpu_type { |  | ||||||
| 	TYPE_S3C2410, |  | ||||||
| 	TYPE_S3C2416, |  | ||||||
| 	TYPE_S3C2443, |  | ||||||
| 	TYPE_S3C64XX, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct s3c_rtc_drv_data { |  | ||||||
| 	int cpu_type; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct s3c_rtc { | struct s3c_rtc { | ||||||
| 	struct device *dev; | 	struct device *dev; | ||||||
| 	struct rtc_device *rtc; | 	struct rtc_device *rtc; | ||||||
|  | @ -51,7 +40,7 @@ struct s3c_rtc { | ||||||
| 	struct clk *rtc_clk; | 	struct clk *rtc_clk; | ||||||
| 	bool enabled; | 	bool enabled; | ||||||
| 
 | 
 | ||||||
| 	enum s3c_cpu_type cpu_type; | 	struct s3c_rtc_data *data; | ||||||
| 
 | 
 | ||||||
| 	int irq_alarm; | 	int irq_alarm; | ||||||
| 	int irq_tick; | 	int irq_tick; | ||||||
|  | @ -63,6 +52,19 @@ struct s3c_rtc { | ||||||
| 	bool wake_en; | 	bool wake_en; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct s3c_rtc_data { | ||||||
|  | 	int max_user_freq; | ||||||
|  | 
 | ||||||
|  | 	void (*irq_handler) (struct s3c_rtc *info, int mask); | ||||||
|  | 	void (*set_freq) (struct s3c_rtc *info, int freq); | ||||||
|  | 	void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); | ||||||
|  | 	void (*select_tick_clk) (struct s3c_rtc *info); | ||||||
|  | 	void (*save_tick_cnt) (struct s3c_rtc *info); | ||||||
|  | 	void (*restore_tick_cnt) (struct s3c_rtc *info); | ||||||
|  | 	void (*enable) (struct s3c_rtc *info); | ||||||
|  | 	void (*disable) (struct s3c_rtc *info); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) | static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) | ||||||
| { | { | ||||||
| 	unsigned long irq_flags; | 	unsigned long irq_flags; | ||||||
|  | @ -83,34 +85,22 @@ static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* IRQ Handlers */ | /* IRQ Handlers */ | ||||||
| static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) |  | ||||||
| { |  | ||||||
| 	struct s3c_rtc *info = (struct s3c_rtc *)id; |  | ||||||
| 
 |  | ||||||
| 	clk_enable(info->rtc_clk); |  | ||||||
| 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); |  | ||||||
| 
 |  | ||||||
| 	if (info->cpu_type == TYPE_S3C64XX) |  | ||||||
| 		writeb(S3C2410_INTP_ALM, info->base + S3C2410_INTP); |  | ||||||
| 
 |  | ||||||
| 	clk_disable(info->rtc_clk); |  | ||||||
| 
 |  | ||||||
| 	s3c_rtc_alarm_clk_enable(info, false); |  | ||||||
| 
 |  | ||||||
| 	return IRQ_HANDLED; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | ||||||
| { | { | ||||||
| 	struct s3c_rtc *info = (struct s3c_rtc *)id; | 	struct s3c_rtc *info = (struct s3c_rtc *)id; | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	if (info->data->irq_handler) | ||||||
| 	rtc_update_irq(info->rtc, 1, RTC_PF | RTC_IRQF); | 		info->data->irq_handler(info, S3C2410_INTP_TIC); | ||||||
| 
 | 
 | ||||||
| 	if (info->cpu_type == TYPE_S3C64XX) | 	return IRQ_HANDLED; | ||||||
| 		writeb(S3C2410_INTP_TIC, info->base + S3C2410_INTP); | } | ||||||
| 
 | 
 | ||||||
| 	clk_disable(info->rtc_clk); | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) | ||||||
|  | { | ||||||
|  | 	struct s3c_rtc *info = (struct s3c_rtc *)id; | ||||||
|  | 
 | ||||||
|  | 	if (info->data->irq_handler) | ||||||
|  | 		info->data->irq_handler(info, S3C2410_INTP_ALM); | ||||||
| 
 | 
 | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
| } | } | ||||||
|  | @ -137,36 +127,18 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Set RTC frequency */ | ||||||
| static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
| { | { | ||||||
| 	unsigned int tmp = 0; |  | ||||||
| 	int val; |  | ||||||
| 
 |  | ||||||
| 	if (!is_power_of_2(freq)) | 	if (!is_power_of_2(freq)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	clk_enable(info->rtc_clk); | ||||||
| 	spin_lock_irq(&info->pie_lock); | 	spin_lock_irq(&info->pie_lock); | ||||||
| 
 | 
 | ||||||
| 	if (info->cpu_type != TYPE_S3C64XX) { | 	if (info->data->set_freq) | ||||||
| 		tmp = readb(info->base + S3C2410_TICNT); | 		info->data->set_freq(info, freq); | ||||||
| 		tmp &= S3C2410_TICNT_ENABLE; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	val = (info->rtc->max_user_freq / freq) - 1; |  | ||||||
| 
 |  | ||||||
| 	if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { |  | ||||||
| 		tmp |= S3C2443_TICNT_PART(val); |  | ||||||
| 		writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); |  | ||||||
| 
 |  | ||||||
| 		if (info->cpu_type == TYPE_S3C2416) |  | ||||||
| 			writel(S3C2416_TICNT2_PART(val), |  | ||||||
| 				info->base + S3C2416_TICNT2); |  | ||||||
| 	} else { |  | ||||||
| 		tmp |= val; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	writel(tmp, info->base + S3C2410_TICNT); |  | ||||||
| 	spin_unlock_irq(&info->pie_lock); | 	spin_unlock_irq(&info->pie_lock); | ||||||
| 	clk_disable(info->rtc_clk); | 	clk_disable(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
|  | @ -174,7 +146,6 @@ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Time read/write */ | /* Time read/write */ | ||||||
| 
 |  | ||||||
| static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | ||||||
| { | { | ||||||
| 	struct s3c_rtc *info = dev_get_drvdata(dev); | 	struct s3c_rtc *info = dev_get_drvdata(dev); | ||||||
|  | @ -355,19 +326,14 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||||
| static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | ||||||
| { | { | ||||||
| 	struct s3c_rtc *info = dev_get_drvdata(dev); | 	struct s3c_rtc *info = dev_get_drvdata(dev); | ||||||
| 	unsigned int ticnt; |  | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	clk_enable(info->rtc_clk); | ||||||
| 	if (info->cpu_type == TYPE_S3C64XX) { |  | ||||||
| 		ticnt = readw(info->base + S3C2410_RTCCON); |  | ||||||
| 		ticnt &= S3C64XX_RTCCON_TICEN; |  | ||||||
| 	} else { |  | ||||||
| 		ticnt = readb(info->base + S3C2410_TICNT); |  | ||||||
| 		ticnt &= S3C2410_TICNT_ENABLE; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no"); | 	if (info->data->enable_tick) | ||||||
|  | 		info->data->enable_tick(info, seq); | ||||||
|  | 
 | ||||||
| 	clk_disable(info->rtc_clk); | 	clk_disable(info->rtc_clk); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -380,50 +346,69 @@ static const struct rtc_class_ops s3c_rtcops = { | ||||||
| 	.alarm_irq_enable = s3c_rtc_setaie, | 	.alarm_irq_enable = s3c_rtc_setaie, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void s3c_rtc_enable(struct s3c_rtc *info, int en) | static void s3c24xx_rtc_enable(struct s3c_rtc *info) | ||||||
| { | { | ||||||
| 	unsigned int con, tmp; | 	unsigned int con, tmp; | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	clk_enable(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
| 	con = readw(info->base + S3C2410_RTCCON); | 	con = readw(info->base + S3C2410_RTCCON); | ||||||
| 	if (!en) { | 	/* re-enable the device, and check it is ok */ | ||||||
| 		if (info->cpu_type == TYPE_S3C64XX) | 	if ((con & S3C2410_RTCCON_RTCEN) == 0) { | ||||||
| 			con &= ~S3C64XX_RTCCON_TICEN; | 		dev_info(info->dev, "rtc disabled, re-enabling\n"); | ||||||
| 		con &= ~S3C2410_RTCCON_RTCEN; |  | ||||||
| 		writew(con, info->base + S3C2410_RTCCON); |  | ||||||
| 
 | 
 | ||||||
| 		if (info->cpu_type != TYPE_S3C64XX) { | 		tmp = readw(info->base + S3C2410_RTCCON); | ||||||
| 			con = readb(info->base + S3C2410_TICNT); | 		writew(tmp | S3C2410_RTCCON_RTCEN, | ||||||
| 			con &= ~S3C2410_TICNT_ENABLE; | 			info->base + S3C2410_RTCCON); | ||||||
| 			writeb(con, info->base + S3C2410_TICNT); |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		/* re-enable the device, and check it is ok */ |  | ||||||
| 		if ((con & S3C2410_RTCCON_RTCEN) == 0) { |  | ||||||
| 			dev_info(info->dev, "rtc disabled, re-enabling\n"); |  | ||||||
| 
 |  | ||||||
| 			tmp = readw(info->base + S3C2410_RTCCON); |  | ||||||
| 			writew(tmp | S3C2410_RTCCON_RTCEN, |  | ||||||
| 				info->base + S3C2410_RTCCON); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (con & S3C2410_RTCCON_CNTSEL) { |  | ||||||
| 			dev_info(info->dev, "removing RTCCON_CNTSEL\n"); |  | ||||||
| 
 |  | ||||||
| 			tmp = readw(info->base + S3C2410_RTCCON); |  | ||||||
| 			writew(tmp & ~S3C2410_RTCCON_CNTSEL, |  | ||||||
| 				info->base + S3C2410_RTCCON); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (con & S3C2410_RTCCON_CLKRST) { |  | ||||||
| 			dev_info(info->dev, "removing RTCCON_CLKRST\n"); |  | ||||||
| 
 |  | ||||||
| 			tmp = readw(info->base + S3C2410_RTCCON); |  | ||||||
| 			writew(tmp & ~S3C2410_RTCCON_CLKRST, |  | ||||||
| 				info->base + S3C2410_RTCCON); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if (con & S3C2410_RTCCON_CNTSEL) { | ||||||
|  | 		dev_info(info->dev, "removing RTCCON_CNTSEL\n"); | ||||||
|  | 
 | ||||||
|  | 		tmp = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 		writew(tmp & ~S3C2410_RTCCON_CNTSEL, | ||||||
|  | 			info->base + S3C2410_RTCCON); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (con & S3C2410_RTCCON_CLKRST) { | ||||||
|  | 		dev_info(info->dev, "removing RTCCON_CLKRST\n"); | ||||||
|  | 
 | ||||||
|  | 		tmp = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 		writew(tmp & ~S3C2410_RTCCON_CLKRST, | ||||||
|  | 			info->base + S3C2410_RTCCON); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	clk_disable(info->rtc_clk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c24xx_rtc_disable(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	unsigned int con; | ||||||
|  | 
 | ||||||
|  | 	clk_enable(info->rtc_clk); | ||||||
|  | 
 | ||||||
|  | 	con = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 	con &= ~S3C2410_RTCCON_RTCEN; | ||||||
|  | 	writew(con, info->base + S3C2410_RTCCON); | ||||||
|  | 
 | ||||||
|  | 	con = readb(info->base + S3C2410_TICNT); | ||||||
|  | 	con &= ~S3C2410_TICNT_ENABLE; | ||||||
|  | 	writeb(con, info->base + S3C2410_TICNT); | ||||||
|  | 
 | ||||||
|  | 	clk_disable(info->rtc_clk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_disable(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	unsigned int con; | ||||||
|  | 
 | ||||||
|  | 	clk_enable(info->rtc_clk); | ||||||
|  | 
 | ||||||
|  | 	con = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 	con &= ~S3C64XX_RTCCON_TICEN; | ||||||
|  | 	con &= ~S3C2410_RTCCON_RTCEN; | ||||||
|  | 	writew(con, info->base + S3C2410_RTCCON); | ||||||
|  | 
 | ||||||
| 	clk_disable(info->rtc_clk); | 	clk_disable(info->rtc_clk); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -441,20 +426,12 @@ static int s3c_rtc_remove(struct platform_device *pdev) | ||||||
| 
 | 
 | ||||||
| static const struct of_device_id s3c_rtc_dt_match[]; | static const struct of_device_id s3c_rtc_dt_match[]; | ||||||
| 
 | 
 | ||||||
| static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) | static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) | ||||||
| { | { | ||||||
| #ifdef CONFIG_OF | 	const struct of_device_id *match; | ||||||
| 	struct s3c_rtc_drv_data *data; |  | ||||||
| 
 | 
 | ||||||
| 	if (pdev->dev.of_node) { | 	match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); | ||||||
| 		const struct of_device_id *match; | 	return (struct s3c_rtc_data *)match->data; | ||||||
| 
 |  | ||||||
| 		match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); |  | ||||||
| 		data = (struct s3c_rtc_drv_data *) match->data; |  | ||||||
| 		return data->cpu_type; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 	return platform_get_device_id(pdev)->driver_data; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int s3c_rtc_probe(struct platform_device *pdev) | static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
|  | @ -463,7 +440,6 @@ static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
| 	struct rtc_time rtc_tm; | 	struct rtc_time rtc_tm; | ||||||
| 	struct resource *res; | 	struct resource *res; | ||||||
| 	int ret; | 	int ret; | ||||||
| 	int tmp; |  | ||||||
| 
 | 
 | ||||||
| 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||||||
| 	if (!info) | 	if (!info) | ||||||
|  | @ -477,7 +453,11 @@ static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	info->dev = &pdev->dev; | 	info->dev = &pdev->dev; | ||||||
| 	info->cpu_type = s3c_rtc_get_driver_data(pdev); | 	info->data = s3c_rtc_get_data(pdev); | ||||||
|  | 	if (!info->data) { | ||||||
|  | 		dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
| 	spin_lock_init(&info->pie_lock); | 	spin_lock_init(&info->pie_lock); | ||||||
| 	spin_lock_init(&info->alarm_clk_lock); | 	spin_lock_init(&info->alarm_clk_lock); | ||||||
| 
 | 
 | ||||||
|  | @ -506,7 +486,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
| 	clk_prepare_enable(info->rtc_clk); | 	clk_prepare_enable(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
| 	/* check to see if everything is setup correctly */ | 	/* check to see if everything is setup correctly */ | ||||||
| 	s3c_rtc_enable(info, 1); | 	if (info->data->enable) | ||||||
|  | 		info->data->enable(info); | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", | 	dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", | ||||||
| 		 readw(info->base + S3C2410_RTCCON)); | 		 readw(info->base + S3C2410_RTCCON)); | ||||||
|  | @ -552,16 +533,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
| 		dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); | 		dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (info->cpu_type != TYPE_S3C2410) | 	if (info->data->select_tick_clk) | ||||||
| 		info->rtc->max_user_freq = 32768; | 		info->data->select_tick_clk(info); | ||||||
| 	else |  | ||||||
| 		info->rtc->max_user_freq = 128; |  | ||||||
| 
 |  | ||||||
| 	if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { |  | ||||||
| 		tmp = readw(info->base + S3C2410_RTCCON); |  | ||||||
| 		tmp |= S3C2443_RTCCON_TICSEL; |  | ||||||
| 		writew(tmp, info->base + S3C2410_RTCCON); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	s3c_rtc_setfreq(info, 1); | 	s3c_rtc_setfreq(info, 1); | ||||||
| 
 | 
 | ||||||
|  | @ -570,7 +543,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
|  err_nortc: |  err_nortc: | ||||||
| 	s3c_rtc_enable(info, 0); | 	if (info->data->disable) | ||||||
|  | 		info->data->disable(info); | ||||||
| 	clk_disable_unprepare(info->rtc_clk); | 	clk_disable_unprepare(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
| 	return ret; | 	return ret; | ||||||
|  | @ -583,15 +557,13 @@ static int s3c_rtc_suspend(struct device *dev) | ||||||
| 	struct s3c_rtc *info = dev_get_drvdata(dev); | 	struct s3c_rtc *info = dev_get_drvdata(dev); | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	clk_enable(info->rtc_clk); | ||||||
|  | 
 | ||||||
| 	/* save TICNT for anyone using periodic interrupts */ | 	/* save TICNT for anyone using periodic interrupts */ | ||||||
| 	if (info->cpu_type == TYPE_S3C64XX) { | 	if (info->data->save_tick_cnt) | ||||||
| 		info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); | 		info->data->save_tick_cnt(info); | ||||||
| 		info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; | 
 | ||||||
| 		info->ticnt_save = readl(info->base + S3C2410_TICNT); | 	if (info->data->disable) | ||||||
| 	} else { | 		info->data->disable(info); | ||||||
| 		info->ticnt_save = readb(info->base + S3C2410_TICNT); |  | ||||||
| 	} |  | ||||||
| 	s3c_rtc_enable(info, 0); |  | ||||||
| 
 | 
 | ||||||
| 	if (device_may_wakeup(dev) && !info->wake_en) { | 	if (device_may_wakeup(dev) && !info->wake_en) { | ||||||
| 		if (enable_irq_wake(info->irq_alarm) == 0) | 		if (enable_irq_wake(info->irq_alarm) == 0) | ||||||
|  | @ -599,6 +571,7 @@ static int s3c_rtc_suspend(struct device *dev) | ||||||
| 		else | 		else | ||||||
| 			dev_err(dev, "enable_irq_wake failed\n"); | 			dev_err(dev, "enable_irq_wake failed\n"); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	clk_disable(info->rtc_clk); | 	clk_disable(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -607,25 +580,20 @@ static int s3c_rtc_suspend(struct device *dev) | ||||||
| static int s3c_rtc_resume(struct device *dev) | static int s3c_rtc_resume(struct device *dev) | ||||||
| { | { | ||||||
| 	struct s3c_rtc *info = dev_get_drvdata(dev); | 	struct s3c_rtc *info = dev_get_drvdata(dev); | ||||||
| 	unsigned int tmp; |  | ||||||
| 
 | 
 | ||||||
| 	clk_enable(info->rtc_clk); | 	clk_enable(info->rtc_clk); | ||||||
| 	s3c_rtc_enable(info, 1); | 
 | ||||||
| 	if (info->cpu_type == TYPE_S3C64XX) { | 	if (info->data->enable) | ||||||
| 		writel(info->ticnt_save, info->base + S3C2410_TICNT); | 		info->data->enable(info); | ||||||
| 		if (info->ticnt_en_save) { | 
 | ||||||
| 			tmp = readw(info->base + S3C2410_RTCCON); | 	if (info->data->restore_tick_cnt) | ||||||
| 			writew(tmp | info->ticnt_en_save, | 		info->data->restore_tick_cnt(info); | ||||||
| 					info->base + S3C2410_RTCCON); |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		writeb(info->ticnt_save, info->base + S3C2410_TICNT); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if (device_may_wakeup(dev) && info->wake_en) { | 	if (device_may_wakeup(dev) && info->wake_en) { | ||||||
| 		disable_irq_wake(info->irq_alarm); | 		disable_irq_wake(info->irq_alarm); | ||||||
| 		info->wake_en = false; | 		info->wake_en = false; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	clk_disable(info->rtc_clk); | 	clk_disable(info->rtc_clk); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -633,56 +601,206 @@ static int s3c_rtc_resume(struct device *dev) | ||||||
| #endif | #endif | ||||||
| static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_OF | static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) | ||||||
| static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { | { | ||||||
| 	[TYPE_S3C2410] = { TYPE_S3C2410 }, | 	clk_enable(info->rtc_clk); | ||||||
| 	[TYPE_S3C2416] = { TYPE_S3C2416 }, | 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); | ||||||
| 	[TYPE_S3C2443] = { TYPE_S3C2443 }, | 	clk_disable(info->rtc_clk); | ||||||
| 	[TYPE_S3C64XX] = { TYPE_S3C64XX }, | 
 | ||||||
|  | 	s3c_rtc_alarm_clk_enable(info, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) | ||||||
|  | { | ||||||
|  | 	clk_enable(info->rtc_clk); | ||||||
|  | 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); | ||||||
|  | 	writeb(mask, info->base + S3C2410_INTP); | ||||||
|  | 	clk_disable(info->rtc_clk); | ||||||
|  | 
 | ||||||
|  | 	s3c_rtc_alarm_clk_enable(info, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
|  | { | ||||||
|  | 	unsigned int tmp = 0; | ||||||
|  | 	int val; | ||||||
|  | 
 | ||||||
|  | 	tmp = readb(info->base + S3C2410_TICNT); | ||||||
|  | 	tmp &= S3C2410_TICNT_ENABLE; | ||||||
|  | 
 | ||||||
|  | 	val = (info->rtc->max_user_freq / freq) - 1; | ||||||
|  | 	tmp |= val; | ||||||
|  | 
 | ||||||
|  | 	writel(tmp, info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
|  | { | ||||||
|  | 	unsigned int tmp = 0; | ||||||
|  | 	int val; | ||||||
|  | 
 | ||||||
|  | 	tmp = readb(info->base + S3C2410_TICNT); | ||||||
|  | 	tmp &= S3C2410_TICNT_ENABLE; | ||||||
|  | 
 | ||||||
|  | 	val = (info->rtc->max_user_freq / freq) - 1; | ||||||
|  | 
 | ||||||
|  | 	tmp |= S3C2443_TICNT_PART(val); | ||||||
|  | 	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||||||
|  | 
 | ||||||
|  | 	writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); | ||||||
|  | 
 | ||||||
|  | 	writel(tmp, info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
|  | { | ||||||
|  | 	unsigned int tmp = 0; | ||||||
|  | 	int val; | ||||||
|  | 
 | ||||||
|  | 	tmp = readb(info->base + S3C2410_TICNT); | ||||||
|  | 	tmp &= S3C2410_TICNT_ENABLE; | ||||||
|  | 
 | ||||||
|  | 	val = (info->rtc->max_user_freq / freq) - 1; | ||||||
|  | 
 | ||||||
|  | 	tmp |= S3C2443_TICNT_PART(val); | ||||||
|  | 	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||||||
|  | 
 | ||||||
|  | 	writel(tmp, info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||||||
|  | { | ||||||
|  | 	int val; | ||||||
|  | 
 | ||||||
|  | 	val = (info->rtc->max_user_freq / freq) - 1; | ||||||
|  | 	writel(val, info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||||||
|  | { | ||||||
|  | 	unsigned int ticnt; | ||||||
|  | 
 | ||||||
|  | 	ticnt = readb(info->base + S3C2410_TICNT); | ||||||
|  | 	ticnt &= S3C2410_TICNT_ENABLE; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	unsigned int con; | ||||||
|  | 
 | ||||||
|  | 	con = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 	con |= S3C2443_RTCCON_TICSEL; | ||||||
|  | 	writew(con, info->base + S3C2410_RTCCON); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||||||
|  | { | ||||||
|  | 	unsigned int ticnt; | ||||||
|  | 
 | ||||||
|  | 	ticnt = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 	ticnt &= S3C64XX_RTCCON_TICEN; | ||||||
|  | 
 | ||||||
|  | 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	info->ticnt_save = readb(info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	writeb(info->ticnt_save, info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 	info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||||||
|  | 	info->ticnt_save = readl(info->base + S3C2410_TICNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||||||
|  | { | ||||||
|  | 	unsigned int con; | ||||||
|  | 
 | ||||||
|  | 	writel(info->ticnt_save, info->base + S3C2410_TICNT); | ||||||
|  | 	if (info->ticnt_en_save) { | ||||||
|  | 		con = readw(info->base + S3C2410_RTCCON); | ||||||
|  | 		writew(con | info->ticnt_en_save, | ||||||
|  | 				info->base + S3C2410_RTCCON); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct s3c_rtc_data const s3c2410_rtc_data = { | ||||||
|  | 	.max_user_freq		= 128, | ||||||
|  | 	.irq_handler		= s3c24xx_rtc_irq, | ||||||
|  | 	.set_freq		= s3c2410_rtc_setfreq, | ||||||
|  | 	.enable_tick		= s3c24xx_rtc_enable_tick, | ||||||
|  | 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt, | ||||||
|  | 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt, | ||||||
|  | 	.enable			= s3c24xx_rtc_enable, | ||||||
|  | 	.disable		= s3c24xx_rtc_disable, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct s3c_rtc_data const s3c2416_rtc_data = { | ||||||
|  | 	.max_user_freq		= 32768, | ||||||
|  | 	.irq_handler		= s3c24xx_rtc_irq, | ||||||
|  | 	.set_freq		= s3c2416_rtc_setfreq, | ||||||
|  | 	.enable_tick		= s3c24xx_rtc_enable_tick, | ||||||
|  | 	.select_tick_clk	= s3c2416_rtc_select_tick_clk, | ||||||
|  | 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt, | ||||||
|  | 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt, | ||||||
|  | 	.enable			= s3c24xx_rtc_enable, | ||||||
|  | 	.disable		= s3c24xx_rtc_disable, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct s3c_rtc_data const s3c2443_rtc_data = { | ||||||
|  | 	.max_user_freq		= 32768, | ||||||
|  | 	.irq_handler		= s3c24xx_rtc_irq, | ||||||
|  | 	.set_freq		= s3c2443_rtc_setfreq, | ||||||
|  | 	.enable_tick		= s3c24xx_rtc_enable_tick, | ||||||
|  | 	.select_tick_clk	= s3c2416_rtc_select_tick_clk, | ||||||
|  | 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt, | ||||||
|  | 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt, | ||||||
|  | 	.enable			= s3c24xx_rtc_enable, | ||||||
|  | 	.disable		= s3c24xx_rtc_disable, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct s3c_rtc_data const s3c6410_rtc_data = { | ||||||
|  | 	.max_user_freq		= 32768, | ||||||
|  | 	.irq_handler		= s3c6410_rtc_irq, | ||||||
|  | 	.set_freq		= s3c6410_rtc_setfreq, | ||||||
|  | 	.enable_tick		= s3c6410_rtc_enable_tick, | ||||||
|  | 	.save_tick_cnt		= s3c6410_rtc_save_tick_cnt, | ||||||
|  | 	.restore_tick_cnt	= s3c6410_rtc_restore_tick_cnt, | ||||||
|  | 	.enable			= s3c24xx_rtc_enable, | ||||||
|  | 	.disable		= s3c6410_rtc_disable, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct of_device_id s3c_rtc_dt_match[] = { | static const struct of_device_id s3c_rtc_dt_match[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.compatible = "samsung,s3c2410-rtc", | 		.compatible = "samsung,s3c2410-rtc", | ||||||
| 		.data = &s3c_rtc_drv_data_array[TYPE_S3C2410], | 		.data = (void *)&s3c2410_rtc_data, | ||||||
| 	}, { | 	}, { | ||||||
| 		.compatible = "samsung,s3c2416-rtc", | 		.compatible = "samsung,s3c2416-rtc", | ||||||
| 		.data = &s3c_rtc_drv_data_array[TYPE_S3C2416], | 		.data = (void *)&s3c2416_rtc_data, | ||||||
| 	}, { | 	}, { | ||||||
| 		.compatible = "samsung,s3c2443-rtc", | 		.compatible = "samsung,s3c2443-rtc", | ||||||
| 		.data = &s3c_rtc_drv_data_array[TYPE_S3C2443], | 		.data = (void *)&s3c2443_rtc_data, | ||||||
| 	}, { | 	}, { | ||||||
| 		.compatible = "samsung,s3c6410-rtc", | 		.compatible = "samsung,s3c6410-rtc", | ||||||
| 		.data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], | 		.data = (void *)&s3c6410_rtc_data, | ||||||
| 	}, | 	}, | ||||||
| 	{}, | 	{ /* sentinel */ }, | ||||||
| }; | }; | ||||||
| MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| static struct platform_device_id s3c_rtc_driver_ids[] = { |  | ||||||
| 	{ |  | ||||||
| 		.name		= "s3c2410-rtc", |  | ||||||
| 		.driver_data	= TYPE_S3C2410, |  | ||||||
| 	}, { |  | ||||||
| 		.name		= "s3c2416-rtc", |  | ||||||
| 		.driver_data	= TYPE_S3C2416, |  | ||||||
| 	}, { |  | ||||||
| 		.name		= "s3c2443-rtc", |  | ||||||
| 		.driver_data	= TYPE_S3C2443, |  | ||||||
| 	}, { |  | ||||||
| 		.name		= "s3c64xx-rtc", |  | ||||||
| 		.driver_data	= TYPE_S3C64XX, |  | ||||||
| 	}, |  | ||||||
| 	{ } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); |  | ||||||
| 
 | 
 | ||||||
| static struct platform_driver s3c_rtc_driver = { | static struct platform_driver s3c_rtc_driver = { | ||||||
| 	.probe		= s3c_rtc_probe, | 	.probe		= s3c_rtc_probe, | ||||||
| 	.remove		= s3c_rtc_remove, | 	.remove		= s3c_rtc_remove, | ||||||
| 	.id_table	= s3c_rtc_driver_ids, |  | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "s3c-rtc", | 		.name	= "s3c-rtc", | ||||||
| 		.owner	= THIS_MODULE, | 		.owner	= THIS_MODULE, | ||||||
|  | @ -690,7 +808,6 @@ static struct platform_driver s3c_rtc_driver = { | ||||||
| 		.of_match_table	= of_match_ptr(s3c_rtc_dt_match), | 		.of_match_table	= of_match_ptr(s3c_rtc_dt_match), | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| module_platform_driver(s3c_rtc_driver); | module_platform_driver(s3c_rtc_driver); | ||||||
| 
 | 
 | ||||||
| MODULE_DESCRIPTION("Samsung S3C RTC Driver"); | MODULE_DESCRIPTION("Samsung S3C RTC Driver"); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Chanwoo Choi
						Chanwoo Choi