| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- #define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/mm.h>
- #include <linux/memory.h>
- #include <linux/memblock.h>
- #include <linux/printk.h>
- #include <linux/sched.h>
- #include <mach/emi_mpu.h>
- #ifdef CONFIG_ARM_LPAE
- #include <mach/mt_lpae.h>
- #endif
- #include "mtkpasr_drv.h"
- /* Struct for parsing rank information (SW view) */
- struct view_rank {
- unsigned long start_pfn; /* The 1st pfn (kernel pfn) */
- unsigned long end_pfn; /* The pfn after the last valid one (kernel pfn) */
- unsigned long bank_pfn_size; /* Bank size in PFN */
- unsigned long valid_channel; /* Channels: 0x00000101 means there are 2 valid channels
- - 1st & 2nd (MAX: 4 channels) */
- };
- static struct view_rank rank_info[MAX_RANKS];
- /* Basic DRAM configuration */
- static struct basic_dram_setting pasrdpd;
- /* MTKPASR control variables */
- static unsigned int channel_count;
- static unsigned int rank_count;
- static unsigned int banks_per_rank;
- static unsigned long mtkpasr_start_pfn;
- static unsigned long mtkpasr_end_pfn;
- static unsigned long mtkpasr_segment_bits;
- #ifdef CONFIG_ARM_LPAE
- #define MAX_KERNEL_PFN (0x13FFFF)
- #define MAX_KPFN_MASK (0x0FFFFF)
- #define KPFN_TO_VIRT (0x100000)
- static unsigned long __init virt_to_kernel_pfn(unsigned long virt)
- {
- unsigned long ret = virt;
- if (enable_4G())
- if (virt > MAX_KERNEL_PFN)
- ret = virt & MAX_KPFN_MASK;
- return ret;
- }
- static unsigned long __init kernel_pfn_to_virt(unsigned long kpfn)
- {
- unsigned long ret = kpfn;
- if (enable_4G())
- ret = kpfn | KPFN_TO_VIRT;
- return ret;
- }
- static unsigned long __init rank_pfn_offset(void)
- {
- unsigned long ret = ARCH_PFN_OFFSET;
- if (enable_4G())
- ret = KPFN_TO_VIRT;
- return ret;
- }
- #else
- #define virt_to_kernel_pfn(x) (x)
- #define kernel_pfn_to_virt(x) (x)
- #define rank_pfn_offset() ((unsigned long)ARCH_PFN_OFFSET)
- #endif
- /* Round up by "base" from "offset" */
- static unsigned long __init round_up_base_offset(unsigned long input, unsigned long base, unsigned long offset)
- {
- return ((input - offset + base - 1) / base) * base + offset;
- }
- /* Round down by "base" from "offset" */
- static unsigned long __init round_down_base_offset(unsigned long input, unsigned long base, unsigned long offset)
- {
- return ((input - offset) / base) * base + offset;
- }
- /*
- * Check DRAM configuration - transform DRAM setting to temporary bank structure.
- * Return 0 on success, -1 on error.
- */
- extern void acquire_dram_setting(struct basic_dram_setting *pasrdpd)__attribute__((weak));
- static int __init check_dram_configuration(void)
- {
- int chan, rank, check_segment_num;
- unsigned long valid_channel;
- unsigned long check_rank_size, rank_pfn, start_pfn = rank_pfn_offset();
- /* Acquire basic DRAM setting */
- acquire_dram_setting(&pasrdpd);
- /* Parse DRAM setting */
- channel_count = pasrdpd.channel_nr;
- for (rank = 0; rank < MAX_RANKS; ++rank) {
- rank_pfn = 0;
- rank_info[rank].valid_channel = 0x0;
- valid_channel = 0x1;
- check_rank_size = 0x0;
- check_segment_num = 0x0;
- for (chan = 0; chan < channel_count; ++chan) {
- if (pasrdpd.channel[chan].rank[rank].valid_rank) {
- /* # Gb -> # pages */
- rank_pfn += (pasrdpd.channel[chan].rank[rank].rank_size << (27 - PAGE_SHIFT));
- rank_info[rank].valid_channel |= valid_channel;
- /* Sanity check for rank size */
- if (!check_rank_size) {
- check_rank_size = pasrdpd.channel[chan].rank[rank].rank_size;
- } else {
- /* We only support ranks with equal size */
- if (check_rank_size != pasrdpd.channel[chan].rank[rank].rank_size)
- return -1;
- }
- /* Sanity check for segment number */
- if (!check_segment_num) {
- check_segment_num = pasrdpd.channel[chan].rank[rank].segment_nr;
- } else {
- /* We only support ranks with equal segment number */
- if (check_segment_num != pasrdpd.channel[chan].rank[rank].segment_nr)
- return -1;
- }
- }
- valid_channel <<= 8;
- }
- /* Have we found a valid rank */
- if (check_rank_size != 0 && check_segment_num != 0) {
- rank_info[rank].start_pfn = virt_to_kernel_pfn(start_pfn);
- rank_info[rank].end_pfn = virt_to_kernel_pfn(start_pfn + rank_pfn);
- rank_info[rank].bank_pfn_size = rank_pfn/check_segment_num;
- start_pfn = kernel_pfn_to_virt(rank_info[rank].end_pfn);
- pr_debug(
- "Rank[%d] start_pfn[%8lu] end_pfn[%8lu] bank_pfn_size[%8lu] valid_channel[0x%-8lx]\n",
- rank, rank_info[rank].start_pfn, rank_info[rank].end_pfn, rank_info[rank].bank_pfn_size,
- rank_info[rank].valid_channel);
- rank_count++;
- banks_per_rank = check_segment_num;
- } else {
- rank_info[rank].start_pfn = virt_to_kernel_pfn(rank_pfn_offset());
- rank_info[rank].end_pfn = virt_to_kernel_pfn(rank_pfn_offset());
- rank_info[rank].bank_pfn_size = 0;
- rank_info[rank].valid_channel = 0x0;
- }
- }
- return 0;
- }
- /*
- * Check whether it is a valid rank
- */
- static bool __init is_valid_rank(int rank)
- {
- /* Check start/end pfn */
- if (rank_info[rank].start_pfn == rank_info[rank].end_pfn)
- return false;
- /* Check valid_channel */
- if (rank_info[rank].valid_channel == 0x0)
- return false;
- return true;
- }
- /*
- * Fill mtkpasr_segment_bits
- */
- static void __init find_mtkpasr_valid_segment(unsigned long start, unsigned long end)
- {
- int num_segment, rank;
- unsigned long spfn, epfn;
- unsigned long rspfn, repfn;
- unsigned long bank_pfn_size;
- num_segment = 0;
- for (rank = 0; rank < MAX_RANKS; ++rank) {
- spfn = kernel_pfn_to_virt(start);
- epfn = kernel_pfn_to_virt(end);
- rspfn = kernel_pfn_to_virt(rank_info[rank].start_pfn);
- repfn = kernel_pfn_to_virt(rank_info[rank].end_pfn);
- if (!is_valid_rank(rank))
- continue;
- bank_pfn_size = rank_info[rank].bank_pfn_size;
- if (epfn >= spfn) {
- /* Intersect */
- if (spfn < repfn && rspfn < epfn) {
- spfn = max(spfn, rspfn);
- epfn = min(epfn, repfn);
- spfn = round_up_base_offset(spfn, bank_pfn_size, rank_pfn_offset());
- epfn = round_down_base_offset(epfn, bank_pfn_size, rank_pfn_offset());
- while (epfn >= (spfn + bank_pfn_size)) {
- mtkpasr_segment_bits |= (1 << ((spfn - rspfn) / bank_pfn_size + num_segment));
- spfn += bank_pfn_size;
- }
- }
- } else {
- /* spfn ~ repfn */
- spfn = max(spfn, rspfn);
- if (spfn < repfn)
- spfn = round_up_base_offset(spfn, bank_pfn_size, rank_pfn_offset());
- while (repfn >= (spfn + bank_pfn_size)) {
- mtkpasr_segment_bits |= (1 << ((spfn - rspfn) / bank_pfn_size + num_segment));
- spfn += bank_pfn_size;
- }
- /* rspfn ~ epfn */
- epfn = min(epfn, repfn);
- if (rspfn < epfn)
- epfn = round_down_base_offset(epfn, bank_pfn_size, rank_pfn_offset());
- while ((epfn - bank_pfn_size) >= rspfn) {
- epfn -= bank_pfn_size;
- mtkpasr_segment_bits |= (1 << ((epfn - rspfn) / bank_pfn_size + num_segment));
- }
- }
- num_segment += 8; /* HW constraint - 8 segment-bits per rank */
- }
- }
- /*
- * We will set an offset on which active PASR will be imposed.
- * This is done by acquiring CMA's base and size.
- * Return <0 means "fail to init pasr range"
- * >0 means "the number of valid banks"
- */
- int __init mtkpasr_init_range(unsigned long start_pfn, unsigned long end_pfn)
- {
- int ret = 0;
- int rank;
- unsigned long pfn_bank_alignment = 0;
- unsigned long vseg, seg_num = 0;
- /* Check DRAM configuration */
- ret = check_dram_configuration();
- if (ret < 0)
- goto out;
- /* Find out which rank "start_pfn" belongs to */
- for (rank = 0; rank < MAX_RANKS; ++rank) {
- if (kernel_pfn_to_virt(start_pfn) < kernel_pfn_to_virt(rank_info[rank].end_pfn) &&
- kernel_pfn_to_virt(start_pfn) >=
- kernel_pfn_to_virt(rank_info[rank].start_pfn)) {
- pfn_bank_alignment = rank_info[rank].bank_pfn_size;
- break;
- }
- }
- /* Sanity check */
- if (!pfn_bank_alignment) {
- ret = -1;
- goto out;
- }
- /* 1st attempted bank size */
- bank_pfns = pfn_bank_alignment;
- /* Find out which rank "end_pfn" belongs to */
- for (rank = 0; rank < MAX_RANKS; ++rank) {
- if (kernel_pfn_to_virt(end_pfn) <= kernel_pfn_to_virt(rank_info[rank].end_pfn) &&
- kernel_pfn_to_virt(end_pfn) >
- kernel_pfn_to_virt(rank_info[rank].start_pfn)) {
- pfn_bank_alignment = rank_info[rank].bank_pfn_size;
- break;
- }
- }
- /* Sanity check: only allow equal bank size */
- if (bank_pfns != pfn_bank_alignment) {
- ret = -2;
- goto out;
- }
- /* Determine mtkpasr_start_pfn/end */
- mtkpasr_start_pfn = round_up_base_offset(start_pfn, pfn_bank_alignment, ARCH_PFN_OFFSET);
- mtkpasr_end_pfn = round_down_base_offset(end_pfn, pfn_bank_alignment, ARCH_PFN_OFFSET);
- /* Map PASR start/end kernel pfn to DRAM segments */
- find_mtkpasr_valid_segment(mtkpasr_start_pfn, mtkpasr_end_pfn);
- /* How many segments */
- vseg = mtkpasr_segment_bits;
- ret = 0;
- do {
- if (vseg & 0x1)
- ret++;
- vseg >>= 1;
- } while (++seg_num < BITS_PER_LONG);
- pr_debug("Start_pfn[%8lu] End_pfn[%8lu] Valid_segment[0x%8lx] Segments[%u]\n",
- mtkpasr_start_pfn, mtkpasr_end_pfn, mtkpasr_segment_bits, ret);
- out:
- return ret;
- }
- /*
- * Give bank, this function will return its (start_pfn, end_pfn) and corresponding rank
- * Return -1 means no valid banks, ranks
- * 0 means no corresponding rank
- * >0 means there are corresponding bank, rank (Caller should subtract 1 to get the correct rank number)
- */
- int __init query_bank_rank_information(int bank, unsigned long *spfn, unsigned long *epfn, int *segn)
- {
- int seg_num = 0, rank, num_segment = 0;
- unsigned long vseg = mtkpasr_segment_bits, vmask;
- /* Reset */
- *spfn = 0;
- *epfn = 0;
- /* Which segment */
- do {
- if (vseg & 0x1) {
- /* Found! */
- if (!bank)
- break;
- bank--;
- }
- vseg >>= 1;
- seg_num++;
- } while (seg_num < BITS_PER_LONG);
- /* Sanity check */
- if (seg_num == BITS_PER_LONG)
- return -1;
- /* Corresponding segment */
- *segn = seg_num;
- /* Which rank */
- vseg = mtkpasr_segment_bits;
- for (rank = 0; rank < MAX_RANKS; ++rank) {
- if (is_valid_rank(rank)) {
- num_segment = (kernel_pfn_to_virt(rank_info[rank].end_pfn) -
- kernel_pfn_to_virt(rank_info[rank].start_pfn)) /
- rank_info[rank].bank_pfn_size;
- if (seg_num < num_segment) {
- *spfn = virt_to_kernel_pfn(kernel_pfn_to_virt(rank_info[rank].start_pfn) +
- seg_num * rank_info[rank].bank_pfn_size);
- *epfn = virt_to_kernel_pfn(kernel_pfn_to_virt(*spfn) +
- rank_info[rank].bank_pfn_size);
- /* Fixup to meet bank range definition ??? */
- if (*epfn <= *spfn)
- *epfn = kernel_pfn_to_virt(*epfn);
- break;
- }
- seg_num -= num_segment;
- vseg >>= num_segment;
- }
- }
- /* Sanity check */
- if (rank == MAX_RANKS)
- return -1;
- /* Query rank information */
- vmask = (1 << num_segment) - 1;
- if ((vseg & vmask) == vmask)
- return (rank + 1);
- return 0;
- }
|