blob: 3fc878ee047bd78675ad348c46e00f07750d390a [file] [log] [blame]
/*
* Copyright (C) ST-Ericsson SA 2011
* Author: Maxime Coquelin <maxime.coquelin@stericsson.com> for ST-Ericsson.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/pasr.h>
#include "helper.h"
enum pasr_state {
PASR_REFRESH,
PASR_NO_REFRESH,
};
struct pasr_fw {
struct pasr_map *map;
};
static struct pasr_fw pasr;
void pasr_update_mask(struct pasr_section *section, enum pasr_state state)
{
struct pasr_die *die = section->die;
phys_addr_t addr = section->start - die->start;
u8 bit = addr >> section_bit;
if (state == PASR_REFRESH)
die->mem_reg &= ~(1 << bit);
else
die->mem_reg |= (1 << bit);
pr_debug("%s(): %s refresh section 0x%08x. segment %#08x Die%d mem_reg = 0x%02x\n"
, __func__, state == PASR_REFRESH ? "Start" : "Stop"
, bit, section->start, die->idx, die->mem_reg);
if (die->apply_mask)
die->apply_mask(&die->mem_reg, die->cookie);
return;
}
void pasr_put(phys_addr_t paddr, unsigned long size)
{
struct pasr_section *s;
unsigned long cur_sz;
unsigned long flags = 0;
if (!pasr.map) {
WARN_ONCE(1, KERN_INFO"%s(): Map not initialized.\n"
"\tCommand line parameters missing or incorrect\n"
, __func__);
goto out;
}
do {
s = pasr_addr2section(pasr.map, paddr);
if (!s)
goto out;
cur_sz = ((paddr + size - 1) < (s->start + section_size - 1)) ?
size : s->start + section_size - paddr;
if (s->lock)
spin_lock_irqsave(s->lock, flags);
s->free_size += cur_sz;
if (s->free_size < section_size)
goto unlock;
BUG_ON(s->free_size > section_size);
if (!s->pair)
pasr_update_mask(s, PASR_NO_REFRESH);
else if (s->pair->free_size == section_size) {
pasr_update_mask(s, PASR_NO_REFRESH);
pasr_update_mask(s->pair, PASR_NO_REFRESH);
}
unlock:
if (s->lock)
spin_unlock_irqrestore(s->lock, flags);
paddr += cur_sz;
size -= cur_sz;
} while (size);
out:
return;
}
void pasr_get(phys_addr_t paddr, unsigned long size)
{
unsigned long flags = 0;
unsigned long cur_sz;
struct pasr_section *s;
if (!pasr.map) {
WARN_ONCE(1, KERN_INFO"%s(): Map not initialized.\n"
"\tCommand line parameters missing or incorrect\n"
, __func__);
return;
}
do {
s = pasr_addr2section(pasr.map, paddr);
if (!s)
goto out;
cur_sz = ((paddr + size - 1) < (s->start + section_size - 1)) ?
size : s->start + section_size - paddr;
if (s->lock)
spin_lock_irqsave(s->lock, flags);
if (s->free_size < section_size)
goto unlock;
if (!s->pair) {
pasr_update_mask(s, PASR_REFRESH);
} else {
pasr_update_mask(s, PASR_REFRESH);
pasr_update_mask(s->pair, PASR_REFRESH);
}
unlock:
BUG_ON(cur_sz > s->free_size);
s->free_size -= cur_sz;
if (s->lock)
spin_unlock_irqrestore(s->lock, flags);
paddr += cur_sz;
size -= cur_sz;
} while (size);
out:
return;
}
int pasr_register_mask_function(phys_addr_t addr, void *function, void *cookie)
{
struct pasr_die *die = pasr_addr2die(pasr.map, addr);
if (!die) {
pr_err("%s: No DDR die corresponding to address 0x%08x\n",
__func__, addr);
return -EINVAL;
}
if (addr != die->start)
pr_warning("%s: Addresses mismatch (Die = 0x%08x, addr = 0x%08x\n"
, __func__, die->start, addr);
die->cookie = cookie;
die->apply_mask = function;
if (die->apply_mask)
die->apply_mask(&die->mem_reg, die->cookie);
return 0;
}
int __init pasr_init_core(struct pasr_map *map)
{
pasr.map = map;
return 0;
}