/*
* OMAP SmartReflex Voltage Control
*
* Author: Thara Gopinath <thara@ti.com>
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Thara Gopinath <thara@ti.com>
*
* Copyright (C) 2008 Nokia Corporation
* Kalle Jokiniemi
*
* Copyright (C) 2007 Texas Instruments, Inc.
* Lesly A M <x0080970@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/interrupt.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <plat/common.h>
#include "pm.h"
#include "smartreflex.h"
#define SMARTREFLEX_NAME_LEN 16
#define NVALUE_NAME_LEN 40
#define SR_DISABLE_TIMEOUT 200
struct omap_sr {
int srid;
int ip_type;
int nvalue_count;
bool autocomp_active;
u32 clk_length;
u32 err_weight;
u32 err_minlimit;
u32 err_maxlimit;
u32 accum_data;
u32 senn_avgweight;
u32 senp_avgweight;
u32 senp_mod;
u32 senn_mod;
unsigned int irq;
void __iomem *base;
struct platform_device *pdev;
struct list_head node;
struct omap_sr_nvalue_table *nvalue_table;
struct voltagedomain *voltdm;
struct dentry *dbg_dir;
};
/* sr_list contains all the instances of smartreflex module */
static LIST_HEAD(sr_list);
static struct omap_sr_class_data *sr_class;
static struct omap_sr_pmic_data *sr_pmic_data;
static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value)
{
__raw_writel(value, (sr->base + offset));
}
static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask,
u32 value)
{
u32 reg_val;
u32 errconfig_offs = 0, errconfig_mask = 0;
reg_val = __raw_readl(sr->base + offset);
reg_val &= ~mask;
/*
* Smartreflex error config register is special as it contains
* certain status bits which if written a 1 into means a clear
* of those bits. So in order to make sure no accidental write of
* 1 happens to those status bits, do a clear of them in the read
* value. This mean this API doesn't rewrite values in these bits
* if they are currently set, but does allow the caller to write
* those bits.
*/
if (sr->ip_type == SR_TYPE_V1) {
errconfig_offs = ERRCONFIG_V1;
errconfig_mask = ERRCONFIG_STATUS_V1_MASK;
} else if (sr->ip_type == SR_TYPE_V2) {
errconfig_offs = ERRCONFIG_V2;
errconfig_mask = ERRCONFIG_VPBOUNDINTST_V2;
}
if (offset == errconfig_offs)
reg_val &= ~errconfig_mask;
reg_val |= value;
__raw_writel(reg_val, (sr->base + offset));
}
static inline u32 sr_read_reg(struct omap_sr *sr, unsigned offset)
{
return __raw_readl(sr->base + offset);
}
static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm)
{
struct omap_sr *sr_info;
if (!voltdm) {
pr_err("%s: Null voltage domain passed!\n", __func__);
return ERR_PTR(-EINVAL);
}
list_for_each_entry(sr_info, &sr_list, node) {
if (voltdm == sr_info->voltdm)
return sr_info;
}
return ERR_PTR(-ENODATA);
}
static irqreturn_t sr_interrupt(int irq, void *data)
{
struct omap_sr *sr_info = (struct omap_sr *)data;
u32 status = 0;
if (sr_info->ip_type == SR_TYPE_V1) {
/* Read the status bits */
status = sr_read_reg(sr_info, ERRCONFIG_V1);
/* Clear them by writing back */
sr_write_reg(sr_info, ERRCONFIG_V1, status);
} else if (sr_info->ip_type == SR_TYPE_V2) {
/* Read the status bits */
sr_read_reg(sr_info, IRQSTATUS);
/* Clear them by writing back */
sr_write_reg(sr_info, IRQSTATUS, status);
}
if (sr_class->notify)
sr_class->notify(sr_info->voltdm, status);
return IRQ_HANDLED;
}
static void sr_set_clk_length(struct omap_sr *sr)
{
struct clk *sys_ck;
u32 sys_clk_speed;