/*
* OMAP SmartReflex Voltage Control
*
* Author: Thara Gopinath <thara@ti.com>
*
* Copyright (C) 2012 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/module.h>
#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 <linux/power/smartreflex.h>
#define DRIVER_NAME "smartreflex"
#define SMARTREFLEX_NAME_LEN 32
#define NVALUE_NAME_LEN 40
#define SR_DISABLE_TIMEOUT 200
/* 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 struct dentry *sr_dbg_dir;
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;
/*
* 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 && offset == ERRCONFIG_V1)
mask |= ERRCONFIG_STATUS_V1_MASK;
else if (sr->ip_type == SR_TYPE_V2 && offset == ERRCONFIG_V2)
mask |= ERRCONFIG_VPBOUNDINTST_V2;
reg_val = __raw_readl(sr->base + offset);
reg_val &= ~mask;
value &= 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 = data;
u32 status = 0;
switch (sr_info->ip_type) {
case 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);
break;
case SR_TYPE_V2:
/* Read the status bits */
status = sr_read_reg(sr_info, IRQSTATUS);
/* Clear them by writing back */
sr_write_reg(sr_info, IRQSTATUS, status);
break;
default:
dev_err(&sr_info->pdev->dev, "UNKNOWN IP type %d\n",
sr_info->ip_type);
return IRQ_NONE;
}
if (sr_class->notify)
sr_class->notify(sr_info, status);
return IRQ_HANDLED;
}
static void sr_set_clk_length(struct omap_sr *sr)
{
struct clk *fck;
u32 fclk_speed;
fck = clk_get(&sr->pdev->dev, "fck");
if (IS_ERR(fck)) {
dev_err(&sr->pdev->dev, "%s: unable to get fck for device %s\n",
__func__, dev_name(&sr->pdev->dev));
return;
}
fclk_speed = clk_get_rate(fck);
clk_put(fck);
switch (fclk_speed) {
case 12000000:
sr<