/*
 *  include/linux/asm-i386/mxt.h
 *
 *  Hardware constants interfacing the Katsina (a.k.a. Pinnacle) chip
 *  and MXT function prototypes here.
 *  See /usr/src/linux/Documentation/mxt.txt 
 *
 *
 *  Copyright (C) 1999,2000 IBM (mxt@us.ibm.com)
 *
 *  This kernel program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  version 2, or later. See /usr/src/linux/COPYING for more detail.
 *
 *  This program is distributed WITHOUT any warranty, merchantability or
 *  fitness for a particular purpose. See /usr/src/linux/COPYING
 *  for more detail.
 */
 
/*
 *  Authors:  Hubertus Franke  frankeh@us.ibm.com
 *            Bulent Abali     abali@us.ibm.com
 */

#include <linux/config.h>
#ifndef __I386_MXT_H
#define __I386_MXT_H

/*
 * physical layout and access of/to the katsina memory controller
 * access registers 
 */

#ifdef __KERNEL__ 

#include <linux/sched.h> /* pick up jiffies for printk */
#define MCPRINTK(fmt, args...)  do{ if (mxt_printk) { printk("<MC>: %ld: ", jiffies);  printk(fmt, ## args); }} while(0)


extern unsigned long get_used_pages(void);
extern unsigned long katsina_init(unsigned long);
extern unsigned long katsina_memutil(void);
extern unsigned long memutil_to_sectors(unsigned long);
extern unsigned long katsina_phys_used(void);

extern void * katsina_vaddr;

#define	KATSINA_PHYS	    DMC30        /* physical address of Katsina  */
/* #define	KATSINA_VADDR	    (fix_to_virt(FIX_KATSINA)) */
#define	KATSINA_VADDR	    katsina_vaddr
#define	KATSINA_LENGTH      0x2000

#define WRITE_CTRL(ADDR,VAL)  *((volatile unsigned long *)(KATSINA_VADDR+(ADDR))) = (unsigned long)(VAL)
#define READ_CTRL(ADDR)       (*((volatile unsigned long *)(KATSINA_VADDR+(ADDR))))

/*
 * Physical memory size should be equal
 * to Real/2 - Real/64 - 1MB of uncompressed space.
 * I should really get the physical mem size from MULR register 
 * of the chip instead
 */

/* Expected compression ratio; how to get this from hardware? */
#define REAL_TO_PHYS_RATIO  2            

/* First 1 MB of the address space is uncompressed */
#define MIN_ADDR_FASTCLEAR  0x00100000   

/* CTT occupies 1/64th of the real memory */
#define CTT_SHIFT           6

/*
 * Real vs Physical memory usage exposed by /proc/sys/mxt/cmpmemi
 */
struct cmpmem_info_t {
	unsigned long memfree;
	unsigned long usedmem;
	unsigned long physused;
	unsigned long util;
	unsigned long cmphold;
	unsigned long cmpheld;
	unsigned long cmphide;
	unsigned long cmphidden;
};

extern void get_cmpmem_info(struct cmpmem_info_t *si);

/*
 * Performance counters exposed by /proc/sys/mxt/cmpperf
 * See Documentation/mxt.txt on how to use /proc/sys/mxt/cmpperf
 */
struct cmpperf_info_t {
	unsigned long mode;
	unsigned long ira;
	unsigned long irax; /* upper 32 bit of soft register */
	unsigned long irb;
	unsigned long irbx; /* upper 32 bit of soft register */
	unsigned long irbl;
	unsigned long irbh;
};

/* extern void cmpperf_info(struct cmpperf_info_t *); */


/*
 * PCI data identifying the memory controller.
 */
#define IBM_PCI_VENDORID_COMPMEM 0x1166
#define IBM_PCI_DEVICEID_COMPMEM 0x12

/* panic() threshold */
#define MC_TH_PANIC 995

/*
 * Chip registers
 */
#define DMC30  0xff000000 /* Mem Mapped Register Base (e.g. DMC30|SUTHR) */
#define SUTHR  0x1450     /* Sector used threshold high register */
#define SUTLR  0x1454     /* Sector used threshold low register */
#define SUR    0x1458     /* Sectors Used Register */
#define RMPR   0x1430     /* Real memory page register. */
#define RMPCR  0x1438     /* Real memory page complement */
#define CCR    0x1434     /* Compression Config */
#define ESR    0x100C     /* Error status register. */
#define ESMR0  0x1010     /* Error status mask register 0. */
#define ESMR1  0x1014     /* Error status mask register 1. */

#define ICR    0x00001024     /* Instrumentation Control */
#define IRA    0x00001028     /* Instrumentation A       */
#define IRB    0x0000102C     /* Instrumentation B       */

/*
 * Sector size definitions.
 * Used with all sector based
 * registers.
 */
#define SECTOR_SIZE       256      /* Number of bytes in a sector.       */
#define SECTOR_BITSHIFT   8        /* Number of bits to shift to convert 
				      from or to a sector based quantity */
                                    
#define MAX_SECTORS  0x1FFFFFF /* Maximum number of sectors 
				  the chipset supports. */
                                    
#define BYTES_TO_SECTORS(b)   ((b) >> SECTOR_BITSHIFT)
#define SECTORS_TO_BYTES(s)   ((s) << SECTOR_BITSHIFT)

/* #define CMP_L3_SIZE  0x2000000  32 MB  */


#define MEM_CTRLR_INTERFACE_TYPE PCIBus  /* Hardware bus type the 
					    memory controller is on. */
#define MEM_CTRLR_BUS_NUM        0       /* Bus number the hardware is on. */
#define MEM_CTRLR_ADDR_SPACE     0       /* Hardware is in memory address 
					    space. */

#define MEM_CTRLR_REGISTER_START 0x1000  /* Start of memory controller 
					    register addresses. */

#define MEM_CTRLR_REGISTER_END   0x152F  /* End of memory controller 
					    register addresses. */


/* 
 * RMPR Layout
 *    3 3 2 2  2 2   2 2  2 2  2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
 *    1 0 9 8  7 6   5 4  3 2  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
 *  +---------+----+-----+----+-------------------------------------------+
 *  | Command |Rsrv|Class|Rsrv|              4KB Page Address             |
 *  +---------+----+-----+----+-------------------------------------------+
 *
 * Commands that may be issued to RMPR;  See the CNB 3.0 Spec RMPR and 
 * RMPCR registers
 */
#define RMPR_CMD_NOOP               0x00000000
#define RMPR_CMD_ZEROPAGE           0x10000000  /* Zero Page Op. 
						   (Invalidate 4K page)
						   Replaces page content
						   w/zeros and removes it 
						   from L3 */
#define RMPR_CMD_FLUSHINVALIDATE    0x20000000  /* Flush-Invalidate 4K Page */
#define RMPR_CMD_TRANSFERCC_CTT     0x30000000  /* Transfer Class Code to 
						   CTT entry. */
#define RMPR_CMD_MOVEZEROSRC_CC     0x40000000  /* Move to complement page 
						   and zero source. 
						   Cache coherent. */
#define RMPR_CMD_MOVEZEROSRC        0x50000000  /* Move to complement page 
						   and zero source. 
						   NOT cache coherent. */
#define RMPR_CMD_SWAPPAGE_CC        0x60000000  /* Swap with complement 
						   page. Cache coherent. */
#define RMPR_CMD_SWAPPAGE           0x70000000  /* Swap with complement page. 
						   NOT Cache coherent. */
#define RMPR_CMD_MOVECTT2LAST       0x80000000  /* Dan Poff's NT hook */
#define RMPR_CMD_INVALIDATE_XFERCC  0x90000000  /* Transfer Class Code 
						   Nobody uses or needs it */
#define RMPR_CMD_MASK               0xf0000000

#define RMPR_CLASSCODE_BITSHIFT     24
#define RMPR_CLASSCODE_BITMASK      3
#define RMPCR_LOCK_BIT              0x80000000

/* Macro to form the request to write from the RMPR. */
#define RMPR_BUILD_REQUEST(cmd, classcode, addr) ((cmd) | ((classcode) << RMPR_CLASSCODE_BITSHIFT) | (addr))

extern void fast_page_op(unsigned long cmd,
			 unsigned long src_page, 
			 unsigned long dst_page, 
			 unsigned long class_code,
			 int wait);

/* Interrupt Definitions...
 */

/* Interrupt which triggers when the memory used threshold has been exceeded.*/
#define HWINT_MEMUSED_THRESHOLD_LINE   0
#define HWINT_MEMUSED_THRESHOLD_PIN    1

/* 
 * Masks that can be applied to the ESR register to determine
 * what error conditions have occurred.
 */
#define ESR_SUTLR_MASK  0x00020000   /* Sectors Used Threshold Low 
					Interrupt fired. */

/* 
 * Masks that can be applied to the ESMR1 register
 * to turn on/off the notifications of error conditions
 */
#define ESMR1_SUTLR_INT 0x00020000   /* Enable Sectors Used Threshold 
					Low Checking. */

#define ESR_IGNORE_MASK  ~(ESR_SUTLR_MASK)  /* interrupts that we are 
					       going to ignore */

static inline void mxt_enable_sutlr_interrupt(void)
{ 
	volatile unsigned long status = READ_CTRL(ESMR1); 
	WRITE_CTRL(ESMR1, status|ESMR1_SUTLR_INT);
}

static inline void mxt_disable_sutlr_interrupt(void)
{ 
	volatile unsigned long status = READ_CTRL(ESMR1);
	WRITE_CTRL(ESMR1, status & ~ESMR1_SUTLR_INT); 
}

static inline void mxt_disable_all_interrupts(void)
{ 
	WRITE_CTRL(ESMR1, 0);
}

static inline void mxt_clear_all_interrupts(void)
{ 
	volatile unsigned long status = READ_CTRL(ESR);
	WRITE_CTRL(ESR, status);
}

static inline void mxt_clear_sutlr_interrupt(void)
{ 
	WRITE_CTRL(ESR, ESR_SUTLR_MASK);
}


/* 
 * compressed memory client callback functions here
 */
typedef enum  _memory_cache_priority_t {
	MEMORY_CACHE_RELEASE,
	MEMORY_CACHE_RELEASE_FORCED,
	MEMORY_CACHE_ZERO
} memory_cache_priority_t;

typedef unsigned long (*memory_cache_callback_fn)(memory_cache_priority_t pri, 
						  unsigned long num_pages);

typedef struct _cmp_client_t {
	struct _cmp_client_t * next;
	memory_cache_priority_t pri;
	memory_cache_callback_fn fn;
} cmp_client_t;

extern int memory_cache_register(memory_cache_priority_t pri, 
				 memory_cache_callback_fn fn );

extern int memory_cache_deregister(int tag);

extern unsigned long cmp_num_physpages;

extern unsigned long perf_counters_request;
extern long swap_reserve;  /* need it in mm/mmap.h */
extern long kernel_reserve;
extern unsigned long mxt_printk;

extern int mxt_bios_found;
extern int mxt_memory_expanded;
extern int mxt_compression_enabled;
extern int mxt_device_found;
extern int mxt_irq;


/* Cover the worst case condition of incompressible 
 * kernel pages by backing them in compressed memory.
 * Discussion per Alan Cox on 5/22/2001 
 */
extern atomic_t nr_hide_pages;
extern atomic_t nr_hidden_pages;
extern void wake_up_hide_pages(void);

#ifndef PG_hidden 
#define PG_hidden PG_skip  /* unused on i386 platforms see include/mm.h */
#endif

static inline void 
remove_hidden_pages(struct page *page, unsigned long order)
{
	int i;

	if ( ! mxt_compression_enabled || ! kernel_reserve ) 
		return;

	if ( ! (page->flags & (1<<PG_hidden)) )
		return;

	for(i=0; i<(1<<order); i++)	
		(page+i)->flags &= ~(1<<PG_hidden);

	atomic_sub(1<<order, &nr_hide_pages);
	
	if ( (atomic_read(&nr_hidden_pages) - atomic_read(&nr_hide_pages)) > (int)(cmp_num_physpages / 32) ) {
		wake_up_hide_pages();
	}
}

static inline void 
add_hidden_pages(struct page *page, unsigned int gfp_mask, unsigned long order)
{
	int i;

	if ( ! mxt_compression_enabled || ! kernel_reserve ) 
		return;

	if( (gfp_mask == GFP_HIGHUSER) || (gfp_mask == GFP_USER) || ! page )
		return;

	if ( atomic_read(&nr_hidden_pages) > kernel_reserve )
		return;  	/* enough pages tucked away */

	/* We decided that this is a kernel page: cannot be
	 * paged out or forced free. Therefore we will hide pages to cover
	 * worst case compressibility
	 */
	atomic_add( 1<<order, &nr_hide_pages);

	/* mark the pages so that we can unhide pages when it is freed */
	for(i=0; i<(1<<order); i++)
		(page+i)->flags |= 1<<PG_hidden;

	/* don't signal until we accumulated enough */
	if ( (atomic_read(&nr_hide_pages) - atomic_read(&nr_hidden_pages)) > 
	     (int)(cmp_num_physpages / 16) ) {
		wake_up_hide_pages();
	}
	return;
}




#endif  /* __KERNEL__ */

#endif  /* __I386_MXT_H */
