summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorRobert Lee <robert.lee@freescale.com>2010-01-18 10:05:02 +0800
committerAlejandro Gonzalez <alex.gonzalez@digi.com>2010-05-24 11:50:03 +0200
commitd03c4f893ae7ca7408ac1576a849393fcf68620a (patch)
tree56474ff190a107429bd1391f4e7ecd8ac52d99e2 /drivers/power
parentc6467c4000e3be01b6159adf392ad32746e2981f (diff)
ENGR00116049-1 [imx23] Addition of FIQ system for chip errata/bo's
[imx23] Addition of FIQ system for chip errata and bo's Signed-off-by: Robert Lee <robert.lee@freescale.com> Signed-off-by: Alejandro Gonzalez <alex.gonzalez@digi.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/stmp37xx/Makefile4
-rw-r--r--drivers/power/stmp37xx/fiq.S108
-rw-r--r--drivers/power/stmp37xx/linux.c159
3 files changed, 242 insertions, 29 deletions
diff --git a/drivers/power/stmp37xx/Makefile b/drivers/power/stmp37xx/Makefile
index 25b340e5b014..65f670efdc93 100644
--- a/drivers/power/stmp37xx/Makefile
+++ b/drivers/power/stmp37xx/Makefile
@@ -5,4 +5,6 @@
obj-$(CONFIG_BATTERY_STMP3XXX) += stmp3xxx-battery.o
stmp3xxx-battery-objs := ddi_bc_api.o ddi_bc_hw.o ddi_bc_init.o \
- ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o
+ ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o \
+ fiq.o
+
diff --git a/drivers/power/stmp37xx/fiq.S b/drivers/power/stmp37xx/fiq.S
new file mode 100644
index 000000000000..09c185dd1536
--- /dev/null
+++ b/drivers/power/stmp37xx/fiq.S
@@ -0,0 +1,108 @@
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/platform.h>
+#include <mach/hardware.h>
+#include <asm/pgtable-hwdef.h>
+#include <mach/regs-power.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-timrot.h>
+
+ .align 5
+ .globl power_fiq_start
+ .globl power_fiq_end
+ .globl power_fiq_count
+ .globl lock_vector_tlb
+
+power_fiq_start:
+
+ ldr r8,power_reg
+ ldr r9,[r8,#HW_POWER_CTRL ]
+ ldr r10,power_off
+
+ @ when VDDIO_BO_IRQ,
+ @ disabled, handled in IRQ for now
+ @tst r9, #BM_POWER_CTRL_VDDIO_BO_IRQ
+
+
+ @ when BATT_BO_IRQ, VDDD_BO_IRQ, VDDA_BO_IRQ, power off chip
+ ldr r11,power_bo
+ tst r9, r11
+ strne r10,[r8,#HW_POWER_RESET]
+
+ @VDD5V_DROOP_IRQ
+ tst r9, #BM_POWER_CTRL_VDD5V_DROOP_IRQ
+ beq check_dcdc4p2
+
+ @ handle errata
+ ldr r10, [r8, #HW_POWER_DCDC4P2]
+ orr r10,r10,#(BM_POWER_DCDC4P2_TRG)
+ orr r10,r10,#(BF_POWER_DCDC4P2_CMPTRIP(31))
+ str r10,[r8, #(HW_POWER_DCDC4P2)]
+
+ @ if battery is below brownout level, shutdown asap
+ ldr r10, [r8, #HW_POWER_STS]
+ tst r10, #BM_POWER_STS_BATT_BO
+ ldr r10, power_off
+ strne r10, [r8, #HW_POWER_RESET]
+
+ @ disable viddio irq
+ mov r11, #BM_POWER_CTRL_ENIRQ_VDDIO_BO
+ str r11, [r8, #HW_POWER_CTRL_CLR]
+
+ @ enable battery BO irq
+ mov r11, #BM_POWER_CTRL_BATT_BO_IRQ
+ str r11, [r8, #HW_POWER_CTRL_CLR]
+ mov r11, #BM_POWER_CTRL_ENIRQBATT_BO
+ str r11, [r8, #HW_POWER_CTRL_SET]
+
+ @ disable dcdc4p2 interrupt
+ mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO
+ str r11, [r8, #HW_POWER_CTRL_CLR]
+
+ @ disable vdd5v_droop interrupt
+ mov r11, #BM_POWER_CTRL_ENIRQ_VDD5V_DROOP
+ str r11, [r8, #HW_POWER_CTRL_CLR]
+
+check_dcdc4p2:
+ @ when DCDC4P2_BO_IRQ,
+ tst r9, #BM_POWER_CTRL_DCDC4P2_BO_IRQ
+
+ mov r11, #BM_POWER_CTRL_BATT_BO_IRQ
+ strne r11, [r8, #HW_POWER_CTRL_CLR]
+
+ mov r11, #BM_POWER_CTRL_ENIRQBATT_BO
+ strne r11, [r8, #HW_POWER_CTRL_SET]
+
+ mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO
+ strne r11, [r8, #HW_POWER_CTRL_CLR]
+
+
+
+ @return from fiq
+ subs pc,lr, #4
+
+power_reg:
+ .long REGS_POWER_BASE
+power_off:
+ .long 0x3e770001
+power_bo:
+ .long BM_POWER_CTRL_BATT_BO_IRQ | \
+ BM_POWER_CTRL_VDDA_BO_IRQ | BM_POWER_CTRL_VDDD_BO_IRQ
+power_fiq_count:
+ .long 0
+power_fiq_end:
+
+lock_vector_tlb:
+
+ mov r1, r0 @ set r1 to the value of the address to be locked down
+ mcr p15,0,r1,c8,c7,1 @ invalidate TLB single entry to ensure that
+ @ LockAddr is not already in the TLB
+ mrc p15,0,r0,c10,c0,0 @ read the lockdown register
+ orr r0,r0,#1 @ set the preserve bit
+ mcr p15,0,r0,c10,c0,0 @ write to the lockdown register
+ ldr r1,[r1] @ TLB will miss, and entry will be loaded
+ mrc p15,0,r0,c10,c0,0 @ read the lockdown register (victim will have
+ @ incremented)
+ bic r0,r0,#1 @ clear preserve bit
+ mcr p15,0,r0,c10,c0,0 @ write to the lockdown registerADR r1,LockAddr
+ mov pc,lr @
diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c
index e4e62f9cb066..8f9703753664 100644
--- a/drivers/power/stmp37xx/linux.c
+++ b/drivers/power/stmp37xx/linux.c
@@ -4,7 +4,7 @@
* Author: Steve Longerbeam <stevel@embeddedalley.com>
*
* Copyright (C) 2008 EmbeddedAlley Solutions Inc.
- * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
*
* 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
@@ -25,9 +25,12 @@
#include <mach/regs-power.h>
#include <mach/regs-usbphy.h>
#include <mach/platform.h>
+#include <mach/irqs.h>
+#include <mach/regs-icoll.h>
#include <linux/delay.h>
-
+#include <linux/proc_fs.h>
#include <linux/interrupt.h>
+#include <asm/fiq.h>
enum application_5v_status{
_5v_connected_verified,
@@ -61,6 +64,9 @@ struct stmp3xxx_info {
uint32_t sm_new_5v_disconnection_jiffies;
enum application_5v_status sm_5v_connection_status;
+
+
+
#define USB_ONLINE 0x01
#define USB_REG_SET 0x02
#define USB_SM_RESTART 0x04
@@ -91,6 +97,8 @@ struct stmp3xxx_info {
#define OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV 3350
#endif
+#define POWER_FIQ
+
/* #define DEBUG_IRQS */
/* There is no direct way to detect wall power presence, so assume the AC
@@ -602,6 +610,7 @@ out:
return ret;
}
+#ifndef POWER_FIQ
static irqreturn_t stmp3xxx_irq_dcdc4p2_bo(int irq, void *cookie)
{
@@ -647,25 +656,29 @@ static irqreturn_t stmp3xxx_irq_vdda_brnout(int irq, void *cookie)
#endif
return IRQ_HANDLED;
}
-static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie)
+
+static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie)
{
#ifdef DEBUG_IRQS
struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
- dev_info(info->dev, "vddio brownout interrupt occurred\n");
- ddi_power_disable_power_interrupts();
-#else
- ddi_power_handle_vddio_brnout();
+ dev_info(info->dev, "vdd5v droop interrupt occurred\n");
#endif
+ ddi_power_handle_vdd5v_droop();
+
return IRQ_HANDLED;
}
-static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie)
+
+#endif /* if POWER_FIQ */
+
+static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie)
{
#ifdef DEBUG_IRQS
struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
- dev_info(info->dev, "vdd5v droop interrupt occurred\n");
+ dev_info(info->dev, "vddio brownout interrupt occurred\n");
+ ddi_power_disable_power_interrupts();
+#else
+ ddi_power_handle_vddio_brnout();
#endif
- ddi_power_handle_vdd5v_droop();
-
return IRQ_HANDLED;
}
@@ -738,6 +751,14 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
goto free_info;
}
+ info->irq_vddio_brnout = platform_get_resource(
+ pdev, IORESOURCE_IRQ, 5);
+ if (info->irq_vddio_brnout == NULL) {
+ printk(KERN_ERR "%s: failed to get irq resouce\n", __func__);
+ goto free_info;
+ }
+
+#ifndef POWER_FIQ
info->irq_dcdc4p2_bo = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (info->irq_dcdc4p2_bo == NULL) {
printk(KERN_ERR "%s: failed to get irq resouce\n", __func__);
@@ -762,19 +783,13 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
goto free_info;
}
- info->irq_vddio_brnout = platform_get_resource(
- pdev, IORESOURCE_IRQ, 5);
- if (info->irq_vddio_brnout == NULL) {
- printk(KERN_ERR "%s: failed to get irq resouce\n", __func__);
- goto free_info;
- }
info->irq_vdd5v_droop = platform_get_resource(pdev, IORESOURCE_IRQ, 6);
if (info->irq_vdd5v_droop == NULL) {
printk(KERN_ERR "%s: failed to get irq resouce\n", __func__);
goto free_info;
}
-
+#endif
platform_set_drvdata(pdev, info);
@@ -828,6 +843,15 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
goto stop_sm;
}
+ ret = request_irq(info->irq_vddio_brnout->start,
+ stmp3xxx_irq_vddio_brnout, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+#ifndef POWER_FIQ
ret = request_irq(info->irq_dcdc4p2_bo->start,
stmp3xxx_irq_dcdc4p2_bo, IRQF_DISABLED,
pdev->name, info);
@@ -860,13 +884,6 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
goto stop_sm;
}
- ret = request_irq(info->irq_vddio_brnout->start,
- stmp3xxx_irq_vddio_brnout, IRQF_DISABLED,
- pdev->name, info);
- if (ret) {
- dev_err(info->dev, "failed to request irq\n");
- goto stop_sm;
- }
ret = request_irq(info->irq_vdd5v_droop->start,
stmp3xxx_irq_vdd5v_droop, IRQF_DISABLED,
@@ -875,7 +892,7 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
dev_err(info->dev, "failed to request irq\n");
goto stop_sm;
}
-
+#endif
ret = power_supply_register(&pdev->dev, &info->bat);
if (ret) {
@@ -912,12 +929,15 @@ unregister_bat:
power_supply_unregister(&info->bat);
free_irq:
free_irq(info->irq_vdd5v->start, pdev);
+ free_irq(info->irq_vddio_brnout->start, pdev);
+#ifndef POWER_FIQ
free_irq(info->irq_dcdc4p2_bo->start, pdev);
free_irq(info->irq_batt_brnout->start, pdev);
free_irq(info->irq_vddd_brnout->start, pdev);
free_irq(info->irq_vdda_brnout->start, pdev);
- free_irq(info->irq_vddio_brnout->start, pdev);
free_irq(info->irq_vdd5v_droop->start, pdev);
+#endif
+
stop_sm:
ddi_bc_ShutDown();
free_info:
@@ -932,12 +952,14 @@ static int stmp3xxx_bat_remove(struct platform_device *pdev)
if (info->regulator)
regulator_put(info->regulator);
free_irq(info->irq_vdd5v->start, pdev);
+ free_irq(info->irq_vddio_brnout->start, pdev);
+#ifndef POWER_FIQ
free_irq(info->irq_dcdc4p2_bo->start, pdev);
free_irq(info->irq_batt_brnout->start, pdev);
free_irq(info->irq_vddd_brnout->start, pdev);
free_irq(info->irq_vdda_brnout->start, pdev);
- free_irq(info->irq_vddio_brnout->start, pdev);
free_irq(info->irq_vdd5v_droop->start, pdev);
+#endif
ddi_bc_ShutDown();
power_supply_unregister(&info->usb);
power_supply_unregister(&info->ac);
@@ -1030,8 +1052,89 @@ static struct platform_driver stmp3xxx_batdrv = {
},
};
+static int power_relinquish(void *data, int relinquish)
+{
+ return -1;
+}
+
+static struct fiq_handler power_fiq = {
+ .name = "stmp3xxx-battery",
+ .fiq_op = power_relinquish
+};
+
+static struct pt_regs fiq_regs;
+extern char power_fiq_start[], power_fiq_end[];
+extern void lock_vector_tlb(void *);
+extern long power_fiq_count;
+static struct proc_dir_entry *power_fiq_proc;
+
static int __init stmp3xxx_bat_init(void)
{
+#ifdef POWER_FIQ
+ int ret;
+ ret = claim_fiq(&power_fiq);
+ if (ret) {
+ pr_err("Can't claim fiq");
+ } else {
+ get_fiq_regs(&fiq_regs);
+ set_fiq_handler(power_fiq_start, power_fiq_end-power_fiq_start);
+ lock_vector_tlb((void *)0xffff0000);
+ lock_vector_tlb(REGS_POWER_BASE);
+
+ /* disable interrupts to be configured as FIQs */
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_DCDC4P2_BO));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_BATT_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDDD_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD18_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD5V_DROOP));
+
+ /* Enable these interrupts as FIQs */
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP));
+
+ /* enable FIQ functionality */
+ __raw_writel(BM_ICOLL_CTRL_FIQ_FINAL_ENABLE,
+ HW_ICOLL_CTRL_SET_ADDR);
+
+ /* enable these interrupts */
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT));
+
+ __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
+ HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP));
+
+ }
+#endif
return platform_driver_register(&stmp3xxx_batdrv);
}