diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/boot/compressed/misc.c /usr/src/sh/7751/cvs/linux/arch/sh/boot/compressed/misc.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/boot/compressed/misc.c Mon Oct 15 22:44:47 2001 +++ /usr/src/sh/7751/cvs/linux/arch/sh/boot/compressed/misc.c Wed May 29 10:41:37 2002 @@ -147,10 +147,28 @@ { sh_bios_console_write(s, strlen(s)); } +#elif defined(CONFIG_SH_EARLY_PUTS_SCIF) +#define TDFE 0x20 +void puts(const char *s) +{ + char *SCFTDR2; + volatile unsigned short *SCFSR2; + + while (*s) { + + SCFTDR2 = (char *)0xffe8000c; + SCFSR2 = (unsigned short *)0xffe80010; + + while ( ((*SCFSR2) & TDFE) == 0 ); + + *SCFTDR2 = *s; + *SCFSR2 = ~TDFE; + s++; + } +} #else void puts(const char *s) { - /* This should be updated to use the sh-sci routines */ } #endif @@ -234,5 +252,5 @@ makecrc(); puts("Uncompressing Linux... "); gunzip(); - puts("Ok, booting the kernel.\n"); + puts("Ok, booting the kernel.\r\n"); } diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/config.in /usr/src/sh/7751/cvs/linux/arch/sh/config.in --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/config.in Fri May 10 20:58:54 2002 +++ /usr/src/sh/7751/cvs/linux/arch/sh/config.in Sun Jun 23 12:21:53 2002 @@ -43,10 +43,11 @@ BigSur CONFIG_SH_BIGSUR \ SH2000 CONFIG_SH_SH2000 \ ADX CONFIG_SH_ADX \ + FlexBox CONFIG_SH_FLEXBOX \ BareCPU CONFIG_SH_UNKNOWN" Generic # The SH7750 RTC module is disabled in the Dreamcast -if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then +if [ "CONFIG_SH_DREAMCAST" = "y" ]; then define_bool CONFIG_SH_RTC n else define_bool CONFIG_SH_RTC y @@ -119,6 +120,11 @@ define_hex CONFIG_MEMORY_SIZE 00400000 define_bool CONFIG_MEMORY_SET y fi +if [ "$CONFIG_SH_FLEXBOX" = "y" ]; then + define_hex CONFIG_MEMORY_START 08000000 + define_hex CONFIG_MEMORY_SIZE 08000000 + define_bool CONFIG_MEMORY_SET y +fi # If none of the above have set memory start/size, ask the user. if [ "$CONFIG_MEMORY_SET" != "y" ]; then hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 @@ -147,7 +153,7 @@ if [ "$CONFIG_SH_GENERIC" = "y" -o "$CONFIG_SH_SOLUTION_ENGINE" = "y" -o \ "$CONFIG_SH_UNKNOWN" = "y" -o "$CONFIG_SH_CAT68701" = "y" -o \ - "$CONFIG_SH_ADX" = "y" ]; then + "$CONFIG_SH_ADX" = "y" -o "$CONFIG_SH_FLEXBOX" = "y" ]; then bool 'Compact Flash Enabler support' CONFIG_CF_ENABLER fi @@ -381,6 +387,7 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Use SCIF for puts() before console' CONFIG_SH_EARLY_PUTS_SCIF bool 'Use LinuxSH standard BIOS' CONFIG_SH_STANDARD_BIOS if [ "$CONFIG_SH_STANDARD_BIOS" = "y" ]; then bool 'Early printk support' CONFIG_SH_EARLY_PRINTK diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/Makefile /usr/src/sh/7751/cvs/linux/arch/sh/kernel/Makefile --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/Makefile Fri May 10 20:58:54 2002 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/Makefile Sun Jun 23 13:00:33 2002 @@ -12,7 +12,8 @@ O_TARGET := kernel.o -export-objs := io.o io_generic.o io_hd64461.o setup_hd64461.o sh_ksyms.o +export-objs := io.o io_generic.o io_hd64461.o setup_hd64461.o sh_ksyms.o \ + rtc-flexbox.o obj-y := process.o signal.o entry.o traps.o irq.o irq_ipr.o \ ptrace.o setup.o time.o sys_sh.o semaphore.o \ @@ -62,6 +63,11 @@ obj-$(CONFIG_SH_UNKNOWN) += mach_unknown.o io_unknown.o machine-specific-objs += mach_unknown.o io_unknown.o + +obj-$(CONFIG_SH_FLEXBOX) += mach_flexbox.o io_flexbox.o pci-flexbox.o setup_flexbox.o \ + rtc-flexbox.o +machine-specific-objs += mach_flexbox.o io_flexbox.o pci-flexbox.o setup_flexbox.o \ + rtc-flexbox.o obj-$(CONFIG_HD64461) += setup_hd64461.o io_hd64461.o machine-specific-objs += setup_hd64461.o io_hd64461.o diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/io_flexbox.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/io_flexbox.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/io_flexbox.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/io_flexbox.c Wed May 29 13:25:03 2002 @@ -0,0 +1,181 @@ +/* + * + * linux/arch/sh/kernel/io_flexbox.c + * + * Copyright (C) 2000 Niibe Yutaka + * + * Generic I/O routine. These can be used where a machine specific version + * is not required. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include + +#define PORT2ADDR(x) (x) + +unsigned long flexbox_io_base; + +static inline void delay(void) +{ + ctrl_inw(0xa0000000); +} + +unsigned char flexbox_inb(unsigned long port) +{ + return *(volatile unsigned char*)PORT2ADDR(port); +} + +unsigned short flexbox_inw(unsigned long port) +{ + return *(volatile unsigned short*)PORT2ADDR(port); +} + +unsigned int flexbox_inl(unsigned long port) +{ + return *(volatile unsigned long*)PORT2ADDR(port); +} + +unsigned char flexbox_inb_p(unsigned long port) +{ + unsigned long v = *(volatile unsigned char*)PORT2ADDR(port); + + delay(); + return v; +} + +unsigned short flexbox_inw_p(unsigned long port) +{ + unsigned long v = *(volatile unsigned short*)PORT2ADDR(port); + + delay(); + return v; +} + +unsigned int flexbox_inl_p(unsigned long port) +{ + unsigned long v = *(volatile unsigned long*)PORT2ADDR(port); + + delay(); + return v; +} + +void flexbox_insb(unsigned long port, void *buffer, unsigned long count) +{ + unsigned char *buf=buffer; + while(count--) *buf++=inb(port); +} + +void flexbox_insw(unsigned long port, void *buffer, unsigned long count) +{ + unsigned short *buf=buffer; + while(count--) *buf++=inw(port); +} + +void flexbox_insl(unsigned long port, void *buffer, unsigned long count) +{ + unsigned long *buf=buffer; + while(count--) *buf++=inl(port); +} + +void flexbox_outb(unsigned char b, unsigned long port) +{ + *(volatile unsigned char*)PORT2ADDR(port) = b; +} + +void flexbox_outw(unsigned short b, unsigned long port) +{ + *(volatile unsigned short*)PORT2ADDR(port) = b; +} + +void flexbox_outl(unsigned int b, unsigned long port) +{ + *(volatile unsigned long*)PORT2ADDR(port) = b; +} + +void flexbox_outb_p(unsigned char b, unsigned long port) +{ + *(volatile unsigned char*)PORT2ADDR(port) = b; + delay(); +} + +void flexbox_outw_p(unsigned short b, unsigned long port) +{ + *(volatile unsigned short*)PORT2ADDR(port) = b; + delay(); +} + +void flexbox_outl_p(unsigned int b, unsigned long port) +{ + *(volatile unsigned long*)PORT2ADDR(port) = b; + delay(); +} + +void flexbox_outsb(unsigned long port, const void *buffer, unsigned long count) +{ + const unsigned char *buf=buffer; + while(count--) outb(*buf++, port); +} + +void flexbox_outsw(unsigned long port, const void *buffer, unsigned long count) +{ + const unsigned short *buf=buffer; + while(count--) outw(*buf++, port); +} + +void flexbox_outsl(unsigned long port, const void *buffer, unsigned long count) +{ + const unsigned long *buf=buffer; + while(count--) outl(*buf++, port); +} + +unsigned char flexbox_readb(unsigned long addr) +{ + return *(volatile unsigned char*)addr; +} + +unsigned short flexbox_readw(unsigned long addr) +{ + return *(volatile unsigned short*)addr; +} + +unsigned int flexbox_readl(unsigned long addr) +{ + return *(volatile unsigned long*)addr; +} + +void flexbox_writeb(unsigned char b, unsigned long addr) +{ + *(volatile unsigned char*)addr = b; +} + +void flexbox_writew(unsigned short b, unsigned long addr) +{ + *(volatile unsigned short*)addr = b; +} + +void flexbox_writel(unsigned int b, unsigned long addr) +{ + *(volatile unsigned long*)addr = b; +} + +void * flexbox_ioremap(unsigned long offset, unsigned long size) +{ + return (void *) (offset); +} + +void flexbox_iounmap(void *addr) +{ +} + +unsigned long flexbox_isa_port2addr(unsigned long offset) +{ + return offset + flexbox_io_base; +} + diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/mach_flexbox.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/mach_flexbox.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/mach_flexbox.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/mach_flexbox.c Sun Jun 23 12:04:57 2002 @@ -0,0 +1,73 @@ +/* + * linux/arch/sh/kernel/mach_flexbox.c + * + * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Machine specific code for FlexBox. + */ + +#include +#include + +#include +#include + +#include + +#include +/* + * The Machine Vector + */ + +extern void flexbox_rtc_gettimeofday(struct timeval *tv); +extern int flexbox_rtc_settimeofday(const struct timeval *tv); +extern int setup_flexbox(void); + +struct sh_machine_vector mv_flexbox __initmv = { + mv_name: "FlexBox", + + mv_nr_irqs: 48, + + mv_inb: flexbox_inb, + mv_inw: flexbox_inw, + mv_inl: flexbox_inl, + mv_outb: flexbox_outb, + mv_outw: flexbox_outw, + mv_outl: flexbox_outl, + + mv_inb_p: flexbox_inb_p, + mv_inw_p: flexbox_inw_p, + mv_inl_p: flexbox_inl_p, + mv_outb_p: flexbox_outb_p, + mv_outw_p: flexbox_outw_p, + mv_outl_p: flexbox_outl_p, + + mv_insb: flexbox_insb, + mv_insw: flexbox_insw, + mv_insl: flexbox_insl, + mv_outsb: flexbox_outsb, + mv_outsw: flexbox_outsw, + mv_outsl: flexbox_outsl, + + mv_readb: flexbox_readb, + mv_readw: flexbox_readw, + mv_readl: flexbox_readl, + mv_writeb: flexbox_writeb, + mv_writew: flexbox_writew, + mv_writel: flexbox_writel, + + mv_ioremap: flexbox_ioremap, + mv_iounmap: flexbox_iounmap, + + mv_isa_port2addr: flexbox_isa_port2addr, + + mv_rtc_gettimeofday: flexbox_rtc_gettimeofday, + mv_rtc_settimeofday: flexbox_rtc_settimeofday, + + mv_init_irq: init_flexbox_IRQ, + mv_init_arch: setup_flexbox, +}; +ALIAS_MV(flexbox) diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/pci-flexbox.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/pci-flexbox.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/pci-flexbox.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/pci-flexbox.c Wed May 29 13:34:52 2002 @@ -0,0 +1,126 @@ +/* + * linux/arch/sh/kernel/pci-flexbox.c + * + * Author: Ian DaSilva (idasilva@mvista.com) + * + * Highly leveraged from pci-bigsur.c, written by Dustin McIntire. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * PCI initialization for the FlexBox. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PCIMCR_MRSET_OFF 0xBFFFFFFF +#define PCIMCR_RFSH_OFF 0xFFFFFFFB + +/* + * Only long word accesses of the PCIC's internal local registers and the + * configuration registers from the CPU is supported. + */ +#define PCIC_WRITE(x,v) writel((v), PCI_REG(x)) +#define PCIC_READ(x) readl(PCI_REG(x)) + +/* + * Description: This function sets up and initializes the pcic, sets + * up the BARS, maps the DRAM into the address space etc, etc. + */ +int __init pcibios_init_platform(void) +{ + unsigned long bcr1, wcr1, wcr2, wcr3, mcr; + unsigned short bcr2; + + /* + * Initialize the slave bus controller on the pcic. The values used + * here should not be hardcoded, but they should be taken from the bsc + * on the processor, to make this function as generic as possible. + * (i.e. Another sbc may usr different SDRAM timing settings -- in order + * for the pcic to work, its settings need to be exactly the same.) + */ + bcr1 = (*(volatile unsigned long*)(SH7751_BCR1)); + bcr2 = (*(volatile unsigned short*)(SH7751_BCR2)); + wcr1 = (*(volatile unsigned long*)(SH7751_WCR1)); + wcr2 = (*(volatile unsigned long*)(SH7751_WCR2)); + wcr3 = (*(volatile unsigned long*)(SH7751_WCR3)); + mcr = (*(volatile unsigned long*)(SH7751_MCR)); + + bcr1 = bcr1 | 0x00080000; /* Enable Bit 19, BREQEN */ + (*(volatile unsigned long*)(SH7751_BCR1)) = bcr1; + + bcr1 = bcr1 | 0x40080000; /* Enable Bit 19 BREQEN, set PCIC to slave */ + PCIC_WRITE(SH7751_PCIBCR1, bcr1); /* PCIC BCR1 */ + PCIC_WRITE(SH7751_PCIBCR2, bcr2); /* PCIC BCR2 */ + PCIC_WRITE(SH7751_PCIWCR1, wcr1); /* PCIC WCR1 */ + PCIC_WRITE(SH7751_PCIWCR2, wcr2); /* PCIC WCR2 */ + PCIC_WRITE(SH7751_PCIWCR3, wcr3); /* PCIC WCR3 */ + mcr = (mcr & PCIMCR_MRSET_OFF) & PCIMCR_RFSH_OFF; + PCIC_WRITE(SH7751_PCIMCR, mcr); /* PCIC MCR */ + + + /* Enable all interrupts, so we know what to fix */ + PCIC_WRITE(SH7751_PCIINTM, 0x0000c3ff); + PCIC_WRITE(SH7751_PCIAINTM, 0x0000380f); + + /* Set up standard PCI config registers */ + PCIC_WRITE(SH7751_PCICONF1, 0xF39000C7); /* Bus Master, Mem & I/O access */ + PCIC_WRITE(SH7751_PCICONF2, 0x00000000); /* PCI Class code & Revision ID */ + PCIC_WRITE(SH7751_PCICONF4, 0xab000001); /* PCI I/O address (local regs) */ + PCIC_WRITE(SH7751_PCICONF5, 0x08000000); /* PCI MEM address (local RAM) */ + PCIC_WRITE(SH7751_PCICONF6, 0xd0000000); /* PCI MEM address (unused) */ + PCIC_WRITE(SH7751_PCICONF11, 0x35051054); /* PCI Subsystem ID & Vendor ID */ + PCIC_WRITE(SH7751_PCILSR0, 0x07f00000); /* MEM (full 64M exposed) */ + PCIC_WRITE(SH7751_PCILSR1, 0x00000000); /* MEM (unused) */ + PCIC_WRITE(SH7751_PCILAR0, 0x08000000); /* MEM (direct map from PCI) */ + PCIC_WRITE(SH7751_PCILAR1, 0x00000000); /* MEM (unused) */ + + /* Now turn it on... */ + PCIC_WRITE(SH7751_PCICR, 0xa5000001); + + /* + * Set PCIMBR and PCIIOBR here, assuming a single window + * (16M MEM, 256K IO) is enough. If a larger space is + * needed, the readx/writex and inx/outx functions will + * have to do more (e.g. setting registers for each call). + */ + + /* + * Set the MBR so PCI address is one-to-one with window, + * meaning all calls go straight through... use ifdef to + * catch erroneous assumption. + */ +#if PCIBIOS_MIN_MEM != SH7751_PCI_MEMORY_BASE +#error One-to-one assumption for PCI memory mapping is wrong!?!?!? +#endif + PCIC_WRITE(SH7751_PCIMBR, PCIBIOS_MIN_MEM); + + /* Set IOBR for window containing area specified in pci.h */ + PCIC_WRITE(SH7751_PCIIOBR, (PCIBIOS_MIN_IO & SH7751_PCIIOBR_MASK)); + + /* All done, may as well say so... */ + printk("SH7751 PCI: Finished initialization of the PCI controller\n"); + + return 1; +} + +int __init pcibios_map_platform_irq(u8 slot, u8 pin) +{ + switch (slot) { + case 13: return 8; + case 14: return 2; + case 15: return 5; + default: + printk("PCI: Bad IRQ mapping request for slot %d\n", slot); + return -1; + } +} + diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/pci-sh7751.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/pci-sh7751.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/pci-sh7751.c Wed Dec 12 02:11:59 2001 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/pci-sh7751.c Wed May 29 13:35:21 2002 @@ -199,11 +199,13 @@ struct pci_ops * __init pci_check_direct(void) { - unsigned int tmp, id; + unsigned int tmp, id, idr; /* check for SH7751 hardware */ id = (SH7751_DEVICE_ID << 16) | SH7751_VENDOR_ID; - if(inl(SH7751_PCIREG_BASE+SH7751_PCICONF0) != id) { + idr = (SH7751R_DEVICE_ID << 16) | SH7751_VENDOR_ID; + if( (inl(SH7751_PCIREG_BASE+SH7751_PCICONF0) != id) && + (inl(SH7751_PCIREG_BASE+SH7751_PCICONF0) != idr) ) { PCIDBG(2,"PCI: This is not an SH7751\n"); return NULL; } diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/rtc-flexbox.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/rtc-flexbox.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/rtc-flexbox.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/rtc-flexbox.c Thu Oct 17 14:47:53 2002 @@ -0,0 +1,372 @@ +/* + * linux/arch/sh/kernel/rtc.c -- Flexbox RTC Ricoh Rx5C348 + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +extern int flex_rev; + +static volatile unsigned char *SCSMR = (unsigned char *) 0xffe00000; +#define SYNC 0x80 + +static volatile unsigned char *SCBRR = (unsigned char *) 0xffe00004; + +static volatile unsigned char *SCSCR = (unsigned char *) 0xffe00008; +#define TE 0x20 +#define RE 0x10 + +static volatile unsigned char *SCTDR = (unsigned char *) 0xffe0000c; +static volatile unsigned char *SCSSR = (unsigned char *) 0xffe00010; +#define TDRE 0x80 +#define RDRF 0x40 +#define ORER 0x20 +#define TEND 0x04 + +static volatile unsigned char *SCRDR = (unsigned char *) 0xffe00014; + +#define rev(c) ( \ + ((c & 0x01) << 7) | ((c & 0x02) << 5) | ((c & 0x04) << 3) | \ + ((c & 0x08) << 1) | ((c & 0x80) >> 7) | ((c & 0x40) >> 5) | \ + ((c & 0x20) >> 3) | ((c & 0x10) >> 1)) +#define set_sci \ + scbrr = *SCBRR; \ + *SCSCR = 0; \ + *SCSMR = SYNC; \ + *SCBRR = 30 + +static volatile unsigned short *SCSPTR2 = (unsigned short *) 0xffe80020; +#define CS0 0xB0 +#define CS1 0xE0 +#define CS01 0xA0 +#define CSNUL 0xF0 + +void set_cs(int card) { + unsigned int i; + volatile unsigned char *ii, *jj; + volatile unsigned char j; + + if(flex_rev==1) { + if(card==0){ + *SCSPTR2 = CS1; + } else if(card==1) { + *SCSPTR2 = CS0; + } else { + *SCSPTR2 = CSNUL; + } + return ; + } + + i = flex_out& ~0x20; + i |= 0x1e; + + if (card==11) { + i |= 0x20; + } else if ((card>-1)&&(card<4)) { + i &= 0xfffef>>card; + } + + flex_out = i; + + ii = (volatile unsigned char *) (i | 0xa4000000); + jj = (volatile unsigned char *) (i | 0xb4000000); + + j=*ii; + j=*jj; +} + +EXPORT_SYMBOL(set_cs); + +static int read_rtc(int t, __u8 *in, __u8 *out) { + + int i, o, s; + + set_cs(11); + + i=0; + o=0; + + *SCSSR = 0; + *SCSCR = TE | RE; + + while(i>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + +void flexbox_rtc_gettimeofday(struct timeval *tv) +{ + unsigned int sec, min, hr, day, mon, yr, yr100; + static int first=1; + + __u8 in[9], out[9]; + int j, scbrr; + + set_sci; + + if(first) { + out[0]=0xe0; + out[1]=0x30; + out[2]=0x88; + read_rtc(3, in, out); + first=0; + } + +again: + + for(j=1;j<9;j++) out[j]=0xff; + out[0]=0xf4; + + read_rtc(9, in, out); + + sec = in[2]; + min = in[3]; + hr = in[4]; + day = in[6]; + mon = in[7] & 0x7f; + yr = in[8]; + yr100 = in[6]&0x80 ? 20 : 19; + + BCD_TO_BIN(yr100); + BCD_TO_BIN(yr); + BCD_TO_BIN(mon); + BCD_TO_BIN(day); + BCD_TO_BIN(hr); + BCD_TO_BIN(min); + BCD_TO_BIN(sec); + + if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 || + hr > 23 || min > 59 || sec > 59) { + printk(KERN_ERR + "5C348 RTC: invalid value, resetting to 1 Jan 2000\n"); + printk(KERN_ERR + "yr=%i mon=%i day=%i hr=%i min=%i sec=%i\n",yr,mon,day,hr,min,sec); + out[0]=0xf0; + out[1]=0x10; + out[2]=0; + out[3]=0; + out[4]=0; + out[5]=0; + out[6]=1; + out[7]=0x81; + out[8]=0; + read_rtc(9, in, out); + goto again; + } + + *SCBRR = scbrr; + + tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec); + tv->tv_usec = 0; +} + +int flexbox_rtc_settimeofday(const struct timeval *tv) +{ + unsigned long nowtime = tv->tv_sec; + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + __u8 in[4], out[4]; + int j, scbrr; + + set_sci; + + for(j=1;j<4;j++) out[j]=0xff; + out[0]=0xf4; + read_rtc(4, in, out); + cmos_minutes = in[3]; + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + for(j=1;j<4;j++) out[j]=0xff; + out[0]=0xf0; + out[1]=0x10; + out[2]=BIN_TO_BCD(real_seconds); + out[3]=BIN_TO_BCD(real_minutes); + read_rtc(4, in, out); + } else { + printk(KERN_WARNING + "set_rtc_time: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + *SCBRR = scbrr; + + return retval; +} + +int CMOS_READ(int addr) { + unsigned char val=0; + __u8 in[4], out[4]; + int j, scbrr; + + if(flex_rev<2) return __CMOS_READ(addr,w); + + set_sci; + + for(j=1;j<4;j++) out[j]=0xff; + + switch(addr) { + case RTC_SECONDS: + out[0]=0x04; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_MINUTES: + out[0]=0x14; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_HOURS: + out[0]=0x24; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_DAY_OF_WEEK: + out[0]=0x34; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_DAY_OF_MONTH: + out[0]=0x44; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_MONTH: + out[0]=0x54; + read_rtc(2, in, out); + val=in[1] & 0x7f; + break; + case RTC_YEAR: + out[0]=0x64; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_SECONDS_ALARM: + val=0; + break; + case RTC_MINUTES_ALARM: + out[0]=0xb4; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_HOURS_ALARM: + out[0]=0xc4; + read_rtc(2, in, out); + val=in[1]; + break; + case RTC_REG_D: /* RTC_VALID */ + /* Always valid ... */ + val = RTC_VRT; + break; + } + + *SCBRR = scbrr; + + return val; +} + +int CMOS_WRITE(int val, int addr) { + __u8 in[4], out[4]; + int j, scbrr; + + if(flex_rev<2) { + __CMOS_WRITE(val, addr, w); + return 0; + } + + set_sci; + + for(j=2;j<4;j++) out[j]=0xff; + out[1]=val; + + switch(addr) { + case RTC_SECONDS: + out[0]=0x08; + read_rtc(2, in, out); + break; + case RTC_MINUTES: + out[0]=0x18; + read_rtc(2, in, out); + break; + case RTC_HOURS: + out[0]=0x28; + read_rtc(2, in, out); + break; + case RTC_DAY_OF_WEEK: + out[0]=0x38; + read_rtc(2, in, out); + break; + case RTC_DAY_OF_MONTH: + out[0]=0x48; + read_rtc(2, in, out); + break; + case RTC_MONTH: + out[0]=0x58; + out[1] |= 0x80; /* 21st century */ + read_rtc(2, in, out); + break; + case RTC_YEAR: + out[0]=0x68; + read_rtc(2, in, out); + break; + case RTC_SECONDS_ALARM: + break; + case RTC_MINUTES_ALARM: + out[0]=0xb8; + read_rtc(2, in, out); + break; + case RTC_HOURS_ALARM: + out[0]=0xc8; + read_rtc(2, in, out); + break; + } + *SCBRR = scbrr; + + return val; +} + + diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/setup_flexbox.c /usr/src/sh/7751/cvs/linux/arch/sh/kernel/setup_flexbox.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/kernel/setup_flexbox.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/arch/sh/kernel/setup_flexbox.c Sat Jun 29 21:10:25 2002 @@ -0,0 +1,66 @@ +/* + * linux/arch/sh/kernel/setup_flexboxx.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +int flex_out; +int flex_rev; + +int __init init_flexbox_IRQ(void) +{ + make_ipr_irq(2, INTC_IPRD, 3, 3); + make_ipr_irq(5, INTC_IPRD, 2, 3); + make_ipr_irq(8, INTC_IPRD, 1, 3); + make_ipr_irq(11, INTC_IPRD, 0, 0); + ctrl_outw(ctrl_inw(INTC_ICR) | INTC_ICR_IRLM, INTC_ICR); /* IRL in edge mode */ + return 0; +} + +int __init setup_flexbox(void) { +#define TIMEOUT 1000000 +#ifdef CONFIG_CPU_SUBTYPE_SH7751 + volatile unsigned char *r64 = (volatile unsigned char *)R64CNT; + int i; + unsigned char j; + + j=*r64; + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CCR 0xff00001c /* Address of Cache Control Register */ + +#define CCR_CACHE_OCE 0x0001 /* Operand Cache Enable */ +#define CCR_CACHE_WT 0x0002 /* Write-Through (for P0,U0,P3) (else writeback)*/ +#define CCR_CACHE_CB 0x0004 /* Copy-Back (for P1) (else writethrough) */ +#define CCR_CACHE_OCI 0x0008 /* OC Invalidate */ +#define CCR_CACHE_ORA 0x0020 /* OC RAM Mode */ +#define CCR_CACHE_OIX 0x0080 /* OC Index Enable */ +#define CCR_CACHE_ICE 0x0100 /* Instruction Cache Enable */ +#define CCR_CACHE_ICI 0x0800 /* IC Invalidate */ +#define CCR_CACHE_IIX 0x8000 /* IC Index Enable */ + +/* Default CCR setup: 8k+16k-byte cache,P1-wb,enable */ +#define CCR_CACHE_VAL (CCR_CACHE_ICE|CCR_CACHE_CB|CCR_CACHE_OCE|0x80000000) +#define CCR_CACHE_INIT (CCR_CACHE_VAL|CCR_CACHE_OCI|CCR_CACHE_ICI) +#define CCR_CACHE_ENABLE (CCR_CACHE_OCE|CCR_CACHE_ICE) + +#define CACHE_IC_ADDRESS_ARRAY 0xf0000000 +#define CACHE_OC_ADDRESS_ARRAY 0xf4000000 +#define CACHE_VALID 1 +#define CACHE_UPDATED 2 + +#define CACHE_OC_WAY_SHIFT 13 +#define CACHE_IC_WAY_SHIFT 13 +#define CACHE_OC_ENTRY_SHIFT 5 +#define CACHE_IC_ENTRY_SHIFT 5 +#define CACHE_OC_ENTRY_MASK 0x3fe0 +#define CACHE_OC_ENTRY_PHYS_MASK 0x0fe0 +#define CACHE_IC_ENTRY_MASK 0x1fe0 +#define CACHE_IC_NUM_ENTRIES 512 +#define CACHE_OC_NUM_ENTRIES 1024 + +static void __init +detect_cpu_and_cache_system(void) +{ +#ifdef CONFIG_CPU_SUBTYPE_ST40STB1 + cpu_data->type = CPU_ST40STB1; +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) + cpu_data->type = CPU_SH7750; +#else +#error Unknown SH4 CPU type +#endif +} + +void __init cache_init(void) +{ + unsigned long ccr; + + detect_cpu_and_cache_system(); + + jump_to_P2(); + ccr = ctrl_inl(CCR); + if (ccr & CCR_CACHE_ENABLE) { + /* + * XXX: Should check RA here. + * If RA was 1, we only need to flush the half of the caches. + */ + unsigned long addr, data; + + for (addr = CACHE_OC_ADDRESS_ARRAY; + addr < (CACHE_OC_ADDRESS_ARRAY+ + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr); + if ((data & (CACHE_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID)) + ctrl_outl(data & ~CACHE_UPDATED, addr); + } + } + + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_P1(); +} + +/* + * SH-4 has virtually indexed and physically tagged cache. + */ + +static struct semaphore p3map_sem[4]; + +void __init p3_cache_init(void) +{ + /* In ioremap.c */ + extern int remap_area_pages(unsigned long address, + unsigned long phys_addr, + unsigned long size, unsigned long flags); + + if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE)) + panic("%s failed.", __FUNCTION__); + sema_init (&p3map_sem[0], 1); + sema_init (&p3map_sem[1], 1); + sema_init (&p3map_sem[2], 1); + sema_init (&p3map_sem[3], 1); +} + +/* + * Write back the dirty D-caches, but not invalidate them. + * + * START: Virtual Address (U0, P1, or P3) + * SIZE: Size of the region. + */ +void __flush_wback_region(void *start, int size) +{ + unsigned long v; + unsigned long begin, end; + + begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); + end = ((unsigned long)start + size + L1_CACHE_BYTES-1) + & ~(L1_CACHE_BYTES-1); + for (v = begin; v < end; v+=L1_CACHE_BYTES) { + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); + } +} + +/* + * Write back the dirty D-caches and invalidate them. + * + * START: Virtual Address (U0, P1, or P3) + * SIZE: Size of the region. + */ +void __flush_purge_region(void *start, int size) +{ + unsigned long v; + unsigned long begin, end; + + begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); + end = ((unsigned long)start + size + L1_CACHE_BYTES-1) + & ~(L1_CACHE_BYTES-1); + for (v = begin; v < end; v+=L1_CACHE_BYTES) { + asm volatile("ocbp %0" + : /* no output */ + : "m" (__m(v))); + } +} + + +/* + * No write back please + */ +void __flush_invalidate_region(void *start, int size) +{ + unsigned long v; + unsigned long begin, end; + + begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); + end = ((unsigned long)start + size + L1_CACHE_BYTES-1) + & ~(L1_CACHE_BYTES-1); + for (v = begin; v < end; v+=L1_CACHE_BYTES) { + asm volatile("ocbi %0" + : /* no output */ + : "m" (__m(v))); + } +} + +/* + * Write back the range of D-cache, and purge the I-cache. + * + * Called from kernel/module.c:sys_init_module and routine for a.out format. + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + flush_cache_all(); +} + +/* + * Write back the D-cache and purge the I-cache for signal trampoline. + */ +void flush_cache_sigtramp(unsigned long addr) +{ + unsigned long v, index; + unsigned long flags; +flush_cache_all(); +return; + + v = addr & ~(L1_CACHE_BYTES-1); + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); + + index = CACHE_IC_ADDRESS_ARRAY| (v&CACHE_IC_ENTRY_MASK); + save_and_cli(flags); + jump_to_P2(); + ctrl_outl(0, index); /* Clear out Valid-bit */ + back_to_P1(); + restore_flags(flags); +} + +static void flush_cache_4096_all(unsigned long start) +{ + unsigned long addr; +flush_cache_all(); +return; + for (addr = start; addr < start + 4096; addr += 32) + ctrl_outl(0, addr); +} + +static inline void flush_cache_4096(unsigned long start, + unsigned long phys) +{ +flush_cache_all(); +return; + if (start >= CACHE_OC_ADDRESS_ARRAY) { + /* + * SH7751 and ST40 have no restriction to handle cache. + * (While SH7750 must do that at P2 area.) + */ + unsigned long addr, data; + for (addr = start; addr < start + 4096; addr += 32) { + data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + } else + { + register unsigned long addr __asm__ ("r4"); + register unsigned long data __asm__ ("r0"); + register unsigned long __r5 __asm__ ("r5") = phys; + register unsigned long __r6 __asm__ ("r6") = (0x1ffff000|CACHE_VALID); + register unsigned long __r7 __asm__ ("r7") = 0; + extern void __flush_cache_4096(unsigned long, unsigned long); + + asm volatile("jsr @%1; nop" + : "=r" (addr), "=r" (data) + : "0" (start), "1" (__flush_cache_4096 + 0x20000000), + "r" (__r5), "r" (__r6), "r" (__r7) + : "pr"); + } +} + +/* + * Write back & invalidate the D-cache of the page. + * (To avoid "alias" issues) + */ +void flush_dcache_page(struct page *page) +{ + if (test_bit(PG_mapped, &page->flags)) { + unsigned long phys = PHYSADDR(page_address(page)); + unsigned long flags; + + phys |= CACHE_VALID; + + save_and_cli(flags); + + /* Loop all the D-cache */ + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys); + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x1000, phys); + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x2000, phys); + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x3000, phys); + + restore_flags(flags); + } +} + +static inline void flush_icache_all(void) +{ + unsigned long flags; + + save_and_cli(flags); + jump_to_P2(); + /* Flush I-cache */ + ctrl_outl(CCR_CACHE_VAL|CCR_CACHE_ICI, CCR); + back_to_P1(); + restore_flags(flags); +} + +void flush_cache_all(void) +{ + extern unsigned long empty_zero_page[1024]; + + /* Prefetch the data to write back D-cache */ + unsigned long addr; + + for (addr = (unsigned long)empty_zero_page; + addr < (unsigned long)empty_zero_page + 1024*16; + addr += L1_CACHE_BYTES) + asm volatile("pref @%0"::"r" (addr)); + flush_icache_all(); +} + +void flush_cache_mm(struct mm_struct *mm) +{ + /* Is there any good way? */ + /* XXX: possibly call flush_cache_range for each vm area */ + /* + * FIXME: Really, the optimal solution here would be able to flush out + * individual lines created by the specified context, but this isn't + * feasible for a number of architectures (such as MIPS, and some + * SPARC) .. is this possible for SuperH? + * + * In the meantime, we'll just flush all of the caches.. this + * seems to be the simplest way to avoid at least a few wasted + * cache flushes. -Lethal + */ + flush_cache_all(); +} + +static void __flush_cache_page(struct vm_area_struct *vma, + unsigned long address, + unsigned long phys) +{ + unsigned long flags; +flush_cache_all(); +return; + phys |= CACHE_VALID; + save_and_cli(flags); + + /* We only need to flush D-cache when we have alias */ + if ((address^phys) & CACHE_ALIAS) { + /* Loop 4K of the D-cache */ + flush_cache_4096( + CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS), + phys); + /* Loop another 4K of the D-cache */ + flush_cache_4096( + CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS), + phys); + } + + if (vma->vm_flags & VM_EXEC) + /* Loop 4K (half) of the I-cache */ + flush_cache_4096( + CACHE_IC_ADDRESS_ARRAY | (address & 0x1000), + phys); + + restore_flags(flags); +} + +/* + * Write back and invalidate D-caches. + * + * START, END: Virtual Address (U0 address) + * + * NOTE: We need to flush the _physical_ page entry. + * Flushing the cache lines for U0 only isn't enough. + * We need to flush for P1 too, which may contain aliases. + */ +void flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long p = start & PAGE_MASK; + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + pte_t entry; + unsigned long phys; + unsigned long d = 0; +flush_cache_all(); +return; + + dir = pgd_offset(mm, p); + pmd = pmd_offset(dir, p); + + do { + if (pmd_none(*pmd) || pmd_bad(*pmd)) { + p &= ~((1 << PMD_SHIFT) -1); + p += (1 << PMD_SHIFT); + pmd++; + continue; + } + pte = pte_offset(pmd, p); + do { + entry = *pte; + if ((pte_val(entry) & _PAGE_PRESENT)) { + phys = pte_val(entry)&PTE_PHYS_MASK; + if ((p^phys) & CACHE_ALIAS) { + d |= 1 << ((p & CACHE_ALIAS)>>12); + d |= 1 << ((phys & CACHE_ALIAS)>>12); + if (d == 0x0f) + goto loop_exit; + } + } + pte++; + p += PAGE_SIZE; + } while (p < end && (unsigned long)pte & PAGE_MASK); + pmd++; + } while (p < end); + loop_exit: + if (d & 1) + flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY); + if (d & 2) + flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x1000); + if (d & 4) + flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x2000); + if (d & 8) + flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x3000); + flush_icache_all(); +} + +/* + * Write back and invalidate I/D-caches for the page. + * + * ADDR: Virtual Address (U0 address) + */ +void flush_cache_page(struct vm_area_struct *vma, unsigned long address) +{ + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + pte_t entry; + unsigned long phys; +flush_cache_all(); +return; + + dir = pgd_offset(vma->vm_mm, address); + pmd = pmd_offset(dir, address); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + return; + pte = pte_offset(pmd, address); + entry = *pte; + if (!(pte_val(entry) & _PAGE_PRESENT)) + return; + + phys = pte_val(entry)&PTE_PHYS_MASK; + __flush_cache_page(vma, address, phys); +} + +/* + * clear_user_page + * @to: P1 address + * @address: U0 address to be mapped + */ +void clear_user_page(void *to, unsigned long address) +{ + struct page *page = virt_to_page(to); + + __set_bit(PG_mapped, &page->flags); +// if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) + clear_page(to); +#if 0 + else { + pgprot_t pgprot = __pgprot(_PAGE_PRESENT | + _PAGE_RW | _PAGE_CACHABLE | + _PAGE_DIRTY | _PAGE_ACCESSED | + _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); + unsigned long phys_addr = PHYSADDR(to); + unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); + pgd_t *dir = pgd_offset_k(p3_addr); + pmd_t *pmd = pmd_offset(dir, p3_addr); + pte_t *pte = pte_offset(pmd, p3_addr); + pte_t entry; + unsigned long flags; + + entry = mk_pte_phys(phys_addr, pgprot); + down(&p3map_sem[(address & CACHE_ALIAS)>>12]); + set_pte(pte, entry); + save_and_cli(flags); + __flush_tlb_page(get_asid(), p3_addr); + restore_flags(flags); + update_mmu_cache(NULL, p3_addr, entry); + __clear_user_page((void *)p3_addr, to); + pte_clear(pte); + up(&p3map_sem[(address & CACHE_ALIAS)>>12]); + } +#endif +} + +/* + * copy_user_page + * @to: P1 address + * @from: P1 address + * @address: U0 address to be mapped + */ +void copy_user_page(void *to, void *from, unsigned long address) +{ + struct page *page = virt_to_page(to); + + __set_bit(PG_mapped, &page->flags); +// if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) + copy_page(to, from); +#if 0 + else { + pgprot_t pgprot = __pgprot(_PAGE_PRESENT | + _PAGE_RW | _PAGE_CACHABLE | + _PAGE_DIRTY | _PAGE_ACCESSED | + _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); + unsigned long phys_addr = PHYSADDR(to); + unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); + pgd_t *dir = pgd_offset_k(p3_addr); + pmd_t *pmd = pmd_offset(dir, p3_addr); + pte_t *pte = pte_offset(pmd, p3_addr); + pte_t entry; + unsigned long flags; + + entry = mk_pte_phys(phys_addr, pgprot); + down(&p3map_sem[(address & CACHE_ALIAS)>>12]); + set_pte(pte, entry); + save_and_cli(flags); + __flush_tlb_page(get_asid(), p3_addr); + restore_flags(flags); + update_mmu_cache(NULL, p3_addr, entry); + __copy_user_page((void *)p3_addr, from, to); + pte_clear(pte); + up(&p3map_sem[(address & CACHE_ALIAS)>>12]); + } +#endif +} diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/mm/cache-sh4.c /usr/src/sh/7751/cvs/linux/arch/sh/mm/cache-sh4.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/arch/sh/mm/cache-sh4.c Wed Apr 3 05:33:16 2002 +++ /usr/src/sh/7751/cvs/linux/arch/sh/mm/cache-sh4.c Thu Jul 4 21:21:01 2002 @@ -1,8 +1,13 @@ -/* $Id: cache-sh4.c,v 1.1.1.1.2.5 2002/04/03 02:33:16 gniibe Exp $ +#ifdef CONFIG_CPU_SUBTYPE_SH7751 +#define CONFIG_SH_CACHE_ASSOC 1 +#endif +/* $Id: cache-sh4.c,v 1.1.1.1 2001/10/15 20:44:53 mrbrown Exp $ + * + * linux/arch/sh/mm/cache.c * - * linux/arch/sh/mm/cache-sh4.c + * Copyright (C) 1999, 2000 Niibe Yutaka * - * Copyright (C) 1999, 2000, 2002 Niibe Yutaka + * Extensions for 2-way associative cache (e.g. SH7751R) by Antony Bowers, April 2002. */ #include @@ -20,7 +25,7 @@ #include #include -#define CCR 0xff00001c /* Address of Cache Control Register */ +#define CCR 0xff00001c /* Address of Cache Control Register */ #define CCR_CACHE_OCE 0x0001 /* Operand Cache Enable */ #define CCR_CACHE_WT 0x0002 /* Write-Through (for P0,U0,P3) (else writeback)*/ @@ -32,33 +37,53 @@ #define CCR_CACHE_ICI 0x0800 /* IC Invalidate */ #define CCR_CACHE_IIX 0x8000 /* IC Index Enable */ -/* Default CCR setup: 8k+16k-byte cache,P1-wb,enable */ -#define CCR_CACHE_VAL (CCR_CACHE_ICE|CCR_CACHE_CB|CCR_CACHE_OCE) -#define CCR_CACHE_INIT (CCR_CACHE_VAL|CCR_CACHE_OCI|CCR_CACHE_ICI) +#if defined(CONFIG_SH_CACHE_ASSOC) +#define CCR_CACHE_EMODE 0x80000000 +#endif + #define CCR_CACHE_ENABLE (CCR_CACHE_OCE|CCR_CACHE_ICE) +#if defined(CONFIG_SH_CACHE_ASSOC) +/* CCR setup for associative mode: 16k+32k 2-way, P1 copy-back, enable */ +#define CCR_CACHE_VAL (CCR_CACHE_EMODE|CCR_CACHE_ENABLE|CCR_CACHE_CB) +#else +/* Default CCR setup: 8k+16k-byte cache, P1-copy-back, enable */ +#define CCR_CACHE_VAL (CCR_CACHE_ENABLE|CCR_CACHE_CB) +#endif + +#define CCR_CACHE_INIT (CCR_CACHE_VAL|CCR_CACHE_OCI|CCR_CACHE_ICI) + #define CACHE_IC_ADDRESS_ARRAY 0xf0000000 #define CACHE_OC_ADDRESS_ARRAY 0xf4000000 + #define CACHE_VALID 1 #define CACHE_UPDATED 2 -#define CACHE_OC_WAY_SHIFT 13 +#define CACHE_OC_WAY_SHIFT 14 #define CACHE_IC_WAY_SHIFT 13 +#define CACHE_OC_WAY_MASK 0x00004000 +#define CACHE_IC_WAY_MASK 0x00002000 + #define CACHE_OC_ENTRY_SHIFT 5 #define CACHE_IC_ENTRY_SHIFT 5 #define CACHE_OC_ENTRY_MASK 0x3fe0 -#define CACHE_OC_ENTRY_PHYS_MASK 0x0fe0 #define CACHE_IC_ENTRY_MASK 0x1fe0 #define CACHE_IC_NUM_ENTRIES 256 #define CACHE_OC_NUM_ENTRIES 512 +/* For 4k page size, these address bits are unchanged by MMU mapping. + */ +#define CACHE_OC_ENTRY_PHYS_MASK 0x0fe0 + static void __init detect_cpu_and_cache_system(void) { -#ifdef CONFIG_CPU_SUBTYPE_ST40STB1 - cpu_data->type = CPU_ST40STB1; +#ifdef CONFIG_CPU_SUBTYPE_ST40 + cpu_data->type = CPU_ST40; #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) cpu_data->type = CPU_SH7750; +#elif defined(CONFIG_CPU_SUBTYPE_SH7751R) + cpu_data->type = CPU_SH7751R; #else #error Unknown SH4 CPU type #endif @@ -71,7 +96,16 @@ detect_cpu_and_cache_system(); jump_to_P2(); + + /* This action is taken if the cache has been enabled by an earlier + * phase of the boot process. + * + * This loop clears the updated (dirty) bit in every cache line that is + * both valid and updated. This forces write-back of those lines. + */ + ccr = ctrl_inl(CCR); + if (ccr & CCR_CACHE_ENABLE) { /* * XXX: Should check RA here. @@ -79,17 +113,43 @@ */ unsigned long addr, data; +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned long way; + + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << + CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + + data = ctrl_inl(addr); + + if ((data & (CACHE_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID)) + ctrl_outl(data & ~CACHE_UPDATED, addr); + } + } + +#else for (addr = CACHE_OC_ADDRESS_ARRAY; addr < (CACHE_OC_ADDRESS_ARRAY+ (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr); + if ((data & (CACHE_UPDATED|CACHE_VALID)) == (CACHE_UPDATED|CACHE_VALID)) ctrl_outl(data & ~CACHE_UPDATED, addr); } +#endif } + /* Invalidate, set cache mode, and enable both IC and OC + */ ctrl_outl(CCR_CACHE_INIT, CCR); back_to_P1(); } @@ -108,7 +168,8 @@ unsigned long size, unsigned long flags); if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE)) - panic("%s failed.", __FUNCTION__); + panic("p3_cache_init failed."); + sema_init (&p3map_sem[0], 1); sema_init (&p3map_sem[1], 1); sema_init (&p3map_sem[2], 1); @@ -159,7 +220,7 @@ /* - * No write back please + * No write back please, just invalidate */ void __flush_invalidate_region(void *start, int size) { @@ -195,76 +256,110 @@ unsigned long flags; v = addr & ~(L1_CACHE_BYTES-1); + asm volatile("ocbwb %0" : /* no output */ : "m" (__m(v))); - index = CACHE_IC_ADDRESS_ARRAY| (v&CACHE_IC_ENTRY_MASK); +/* The code below invalidates the IC line with the given index, regardless + * of whether or not the address is a hit. This appears suboptimal because an + * associative write would invalidate only under hit, like the ocbwb + * instruction does for the OC. + * + * However, an associative write simply does nothing if the address causes a + * TLB miss, and is therefore unsafe. It could only be used if there was some + * way to force the appropriate entry to be in the TLB in advance. + * + * AB + */ + index = CACHE_IC_ADDRESS_ARRAY | (v & CACHE_IC_ENTRY_MASK); + save_and_cli(flags); jump_to_P2(); + ctrl_outl(0, index); /* Clear out Valid-bit */ + +#if defined(CONFIG_SH_CACHE_ASSOC) + /* Must invalidate both ways for associative cache */ + ctrl_outl(0, index | (1 << CACHE_IC_WAY_SHIFT)); +#endif + back_to_P1(); restore_flags(flags); } -static void flush_cache_4096_all(unsigned long start) +/* + * Writeback&Invalidate the D-cache of the page + */ +static void __flush_dcache_page(unsigned long phys) { -#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40STB1) - /* - * SH7751 and ST40 have no restriction to handle cache. - * (While SH7750 must do that at P2 area.) - */ - unsigned long addr; - for (addr = start; addr < start + 4096; addr += 32) - ctrl_outl(0, addr); -#else - register unsigned long __r0 __asm__ ("r0") = 0; - register unsigned long __r1 __asm__ ("r1") = 128; - register unsigned long __r4 __asm__ ("r4"); - register unsigned long __r5 __asm__ ("r5"); - register unsigned long __r6 __asm__ ("r6"); - register unsigned long __r7 __asm__ ("r7"); - extern void __flush_cache_4096_all(unsigned long); - - asm volatile("jsr @%7; nop" - : "=&r" (__r4), "=&r" (__r5), "=&r" (__r6), "=&r" (__r7) - : "0" (start), "r" (__r0), "r" (__r1), - "r" (__flush_cache_4096_all + 0x20000000) - : "pr"); + unsigned long addr, data; + unsigned long flags; + +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned int way; #endif -} -static inline void flush_cache_4096(unsigned long start, - unsigned long phys) -{ -#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40STB1) - if (start >= CACHE_OC_ADDRESS_ARRAY) { - /* - * SH7751 and ST40 have no restriction to handle cache. - * (While SH7750 must do that at P2 area.) - */ - unsigned long addr, data; - for (addr = start; addr < start + 4096; addr += 32) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - } else + phys |= CACHE_VALID; + +/* Here, phys is the physical address of the page. We check all the tags in + * the cache for those with the same page number as this page (by masking off + * the lowest 2 bits of the 19-bit tag; these bits are derived from the offset + * within in the 4k page). Matching valid entries are invalidated. + * + * Since 2 bits of the cache index are derived from the virtual page number, + * knowing this would reduce the number of cache entries to be searched by a + * factor of 4. However this function exists to deal with potential cache + * aliasing, therefore the optimisation is probably not possible. + */ + save_and_cli(flags); + jump_to_P2(); + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY; + addr < (CACHE_OC_ADDRESS_ARRAY + + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr) & (0x1ffff000 | CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + +#if defined(CONFIG_SH_CACHE_ASSOC) + /* In associative mode, we need to check both ways */ + for (way = 0; way <= 1; ++way) { + unsigned int waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr) & (0x1ffff000 | CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + } #endif - { - register unsigned long addr __asm__ ("r4"); - register unsigned long data __asm__ ("r0"); - register unsigned long __r5 __asm__ ("r5") = phys; - register unsigned long __r6 __asm__ ("r6") = (0x1ffff000|CACHE_VALID); - register unsigned long __r7 __asm__ ("r7") = 0; - extern void __flush_cache_4096(unsigned long, unsigned long); - - asm volatile("jsr @%1; nop" - : "=r" (addr), "=r" (data) - : "0" (start), "1" (__flush_cache_4096 + 0x20000000), - "r" (__r5), "r" (__r6), "r" (__r7) - : "pr"); + +#if 0 /* DEBUG DEBUG */ + /* Loop all the I-cache */ + for (addr = CACHE_IC_ADDRESS_ARRAY; + addr < (CACHE_IC_ADDRESS_ARRAY + +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); + addr += (1<flags)) { - unsigned long phys = PHYSADDR(page_address(page)); - unsigned long flags; - - phys |= CACHE_VALID; - - save_and_cli(flags); - - /* Loop all the D-cache */ - flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys); - flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x1000, phys); - flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x2000, phys); - flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x3000, phys); - - restore_flags(flags); - } + if (test_bit(PG_mapped, &page->flags)) + __flush_dcache_page(PHYSADDR(page_address(page))); } -static inline void flush_icache_all(void) +void flush_cache_all(void) { unsigned long flags; + unsigned long addr; + +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned long way; +#else + extern unsigned long empty_zero_page[1024]; +#endif save_and_cli(flags); - jump_to_P2(); - /* Flush I-cache */ - ctrl_outl(CCR_CACHE_VAL|CCR_CACHE_ICI, CCR); - back_to_P1(); - restore_flags(flags); -} -#undef C_IMPLEMENTATION_OF_CACHE_ALL +#if defined(CONFIG_SH_CACHE_ASSOC) + jump_to_P2(); -void flush_cache_all(void) -{ - extern unsigned long empty_zero_page[1024]; + /* Clear the U and V bits for each line and each way. On SH-4, this + * causes write-back if both U and V are set before the address write. + */ + + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + ctrl_outl(0, addr); + } + } - /* Prefetch the data to write back D-cache */ +#else /* !defined(CONFIG_SH_CACHE_ASSOC) */ -#ifdef C_IMPLEMENTATION_OF_CACHE_ALL - unsigned long addr; + /* Why do it this way? + * + * This code makes an unjustifiable assumption that 16k of + * memory starting at empty_zero_page is pre-fetchable. It should also + * be rather inefficient. + */ + /* Prefetch the data to write back D-cache */ for (addr = (unsigned long)empty_zero_page; addr < (unsigned long)empty_zero_page + 1024*16; - addr += L1_CACHE_BYTES) + addr += L1_CACHE_BYTES) { asm volatile("pref @%0"::"r" (addr)); -#else - unsigned long a0, a1, a2, a3, cnt; - asm volatile( - "mov %0, %1; add #32, %1\n\t" - "mov %0, %2; add #64, %2\n\t" - "mov %1, %3; add #64, %3\n\t" - "1:\n\t" - "pref @%0\n\t" - "dt %4\n\t" - "pref @%1\n\t" - "add %5, %0\n\t" - "pref @%2\n\t" - "add %5, %1\n\t" - "pref @%3\n\t" - "add %5, %2\n\t" - "bf/s 1b\n\t" - " add %5, %3" - : "=&r" (a0), "=&r" (a1), "=&r" (a2), "=&r" (a3), "=&r" (cnt) - : "r" (32*4), "0" (empty_zero_page), "4" (1024*16/32/4) - : "t"); + } + + jump_to_P2(); #endif - flush_icache_all(); + /* Flush D-cache/I-cache */ + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_P1(); + restore_flags(flags); } void flush_cache_mm(struct mm_struct *mm) { /* Is there any good way? */ /* XXX: possibly call flush_cache_range for each vm area */ - /* - * FIXME: Really, the optimal solution here would be able to flush out - * individual lines created by the specified context, but this isn't - * feasible for a number of architectures (such as MIPS, and some - * SPARC) .. is this possible for SuperH? - * - * In the meantime, we'll just flush all of the caches.. this - * seems to be the simplest way to avoid at least a few wasted - * cache flushes. -Lethal - */ flush_cache_all(); } -static void __flush_cache_page(struct vm_area_struct *vma, - unsigned long address, - unsigned long phys) -{ - unsigned long flags; - - phys |= CACHE_VALID; - save_and_cli(flags); - - /* We only need to flush D-cache when we have alias */ - if ((address^phys) & CACHE_ALIAS) { - /* Loop 4K of the D-cache */ - flush_cache_4096( - CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS), - phys); - /* Loop another 4K of the D-cache */ - flush_cache_4096( - CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS), - phys); - } - - if (vma->vm_flags & VM_EXEC) - /* Loop 4K (half) of the I-cache */ - flush_cache_4096( - CACHE_IC_ADDRESS_ARRAY | (address & 0x1000), - phys); - - restore_flags(flags); -} - /* * Write back and invalidate D-caches. * @@ -401,65 +447,32 @@ void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - unsigned long p = start & PAGE_MASK; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - pte_t entry; - unsigned long phys; - unsigned long d = 0; - - dir = pgd_offset(mm, p); - pmd = pmd_offset(dir, p); - - do { - if (pmd_none(*pmd) || pmd_bad(*pmd)) { - p &= ~((1 << PMD_SHIFT) -1); - p += (1 << PMD_SHIFT); - pmd++; - continue; - } - pte = pte_offset(pmd, p); - do { - entry = *pte; - if ((pte_val(entry) & _PAGE_PRESENT)) { - phys = pte_val(entry)&PTE_PHYS_MASK; - if ((p^phys) & CACHE_ALIAS) { - d |= 1 << ((p & CACHE_ALIAS)>>12); - d |= 1 << ((phys & CACHE_ALIAS)>>12); - if (d == 0x0f) - goto loop_exit; - } - } - pte++; - p += PAGE_SIZE; - } while (p < end && (unsigned long)pte & PAGE_MASK); - pmd++; - } while (p < end); - loop_exit: - if (d & 1) - flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY); - if (d & 2) - flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x1000); - if (d & 4) - flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x2000); - if (d & 8) - flush_cache_4096_all(CACHE_OC_ADDRESS_ARRAY | 0x3000); - flush_icache_all(); + /* + * We could call flush_cache_page for the pages of these range, + * but it's not efficient (scan the caches all the time...). + * + * We can't use A-bit magic, as there's the case we don't have + * valid entry on TLB. + */ + flush_cache_all(); } +#define CACHE_LINES_PER_PAGE (PAGE_SIZE / L1_CACHE_BYTES) + /* * Write back and invalidate I/D-caches for the page. * * ADDR: Virtual Address (U0 address) */ + void flush_cache_page(struct vm_area_struct *vma, unsigned long address) { pgd_t *dir; pmd_t *pmd; pte_t *pte; pte_t entry; - unsigned long phys; + unsigned long phys, addr, data; + unsigned long flags; dir = pgd_offset(vma->vm_mm, address); pmd = pmd_offset(dir, address); @@ -467,11 +480,130 @@ return; pte = pte_offset(pmd, address); entry = *pte; - if (!(pte_val(entry) & _PAGE_PRESENT)) + if (pte_none(entry) || !pte_present(entry)) return; phys = pte_val(entry)&PTE_PHYS_MASK; - __flush_cache_page(vma, address, phys); + + phys |= CACHE_VALID; + save_and_cli(flags); + jump_to_P2(); + + /* This code is obscure. It seems to be looking for aliasing between + * a physical address and a virtual address, and using the physical + * address to index the cache. + * + * We guess that, in fact, the physical address (phys) represents the + * effective address in kernel space of a page that is shared between + * kernel space and user space. Hence the potential alias. + * + * AB + */ + + /* We only need to flush D-cache when we have alias */ + if ((address^phys) & CACHE_ALIAS) { +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned long way; + + /* Check both ways of associative cache */ + + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop 4K of the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + + (address & CACHE_ALIAS) + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + + (address & CACHE_ALIAS) + waybit + + (CACHE_LINES_PER_PAGE << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr) & (0x1ffff000 | CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + + /* Loop another 4K of the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + + (phys & CACHE_ALIAS) + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + + (phys & CACHE_ALIAS) + waybit + + (CACHE_LINES_PER_PAGE << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr) & (0x1ffff000 | CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + } +#else + + /* FIXME: this makes an assumption that one page + * worth of cache entries is the total number of cache entries + * divided by 4 (or 2 for the IC). + * + * This happens to be true for some SH-4 cache implementations + * and a 4K page size, but only by coincidence. + * + * AB + */ + + /* Loop 4K of the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS); + addr < (CACHE_OC_ADDRESS_ARRAY + (address & CACHE_ALIAS) + +(CACHE_OC_NUM_ENTRIES/4<vm_flags & VM_EXEC) { + +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned long way; + + /* Check both ways of associative cache */ + + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop 4K of the I-cache */ + for (addr = CACHE_IC_ADDRESS_ARRAY + + (address & 0x1000) + waybit; + addr < (CACHE_IC_ADDRESS_ARRAY + + (address & 0x1000) + waybit + + (CACHE_LINES_PER_PAGE << CACHE_IC_ENTRY_SHIFT)); + addr += (1 << CACHE_IC_ENTRY_SHIFT)) { + data = ctrl_inl(addr) & (0x1ffff000 | CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + } +#else + /* Loop 4K of the I-cache */ + for (addr = CACHE_IC_ADDRESS_ARRAY|(address&0x1000); + addr < ((CACHE_IC_ADDRESS_ARRAY|(address&0x1000)) + +(CACHE_IC_NUM_ENTRIES/2< #include #include +#include #include @@ -76,8 +77,63 @@ copy_to: physmap_copy_to }; +#ifdef CONFIG_SH_FLEXBOX +#ifdef CONFIG_CPU_SUBTYPE_SH7751 +static struct mtd_partition marius_partitions[] = { + { + name: "blob", + size: 0x00010000, + offset: 0x00000000, + },{ + name: "kernel", + size: 0x000c0000, + offset: MTDPART_OFS_APPEND, + },{ + name: "initrd", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#else +static struct mtd_partition marius_partitions[] = { + { + name: "blob", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + },{ + name: "kernel", + size: 0x000c0000, + offset: MTDPART_OFS_APPEND, + },{ + name: "initrd", + size: 0x00580000, + offset: MTDPART_OFS_APPEND, + },{ + name: "opt", + size: 0x00680000, + offset: MTDPART_OFS_APPEND, + },{ + name: "etc1", + size: 0x00180000, + offset: MTDPART_OFS_APPEND, + },{ + name: "etc2", + size: 0x00180000, + offset: MTDPART_OFS_APPEND, + },{ + name: "extra", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif /* 7751 */ +#endif /* flexbox */ +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + int __init init_physmap(void) { + int nb_parts; + printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); @@ -89,11 +145,15 @@ if (mymtd) { mymtd->module = THIS_MODULE; + nb_parts = NB_OF(marius_partitions); + if(nb_parts) { + add_mtd_partitions(mymtd, marius_partitions, nb_parts); + } else add_mtd_device(mymtd); return 0; } - iounmap((void *)physmap_map.map_priv_1); +// iounmap((void *)physmap_map.map_priv_1); return -ENXIO; } diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/net/natsemi.c /usr/src/sh/7751/cvs/linux/drivers/net/natsemi.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/net/natsemi.c Mon Feb 25 21:37:59 2002 +++ /usr/src/sh/7751/cvs/linux/drivers/net/natsemi.c Thu Aug 15 13:05:43 2002 @@ -743,6 +743,7 @@ } /* Work around the dropped serial bit. */ +#if 0 prev_eedata = eeprom_read(ioaddr, 6); for (i = 0; i < 3; i++) { int eedata = eeprom_read(ioaddr, i + 7); @@ -750,7 +751,15 @@ dev->dev_addr[i*2+1] = eedata >> 7; prev_eedata = eedata; } - +#else + dev->dev_addr[0]=0; + dev->dev_addr[1]=0; + dev->dev_addr[2]=find_cnt+0x10; + dev->dev_addr[3]=find_cnt<<4; + dev->dev_addr[4]=0; + dev->dev_addr[5]=0; +#endif + dev->base_addr = ioaddr; dev->irq = irq; @@ -765,6 +774,11 @@ /* Reset the chip to erase previous misconfiguration. */ natsemi_reload_eeprom(dev); natsemi_reset(dev); + + i=readl(ioaddr + ChipConfig); + writel( (i&~0xe400) | 0xe400, ioaddr + ChipConfig); + udelay(10); + writel( (i&~0xe400) | 0xe000, ioaddr + ChipConfig); option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; if (dev->mem_start) diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/Config.in /usr/src/sh/7751/cvs/linux/drivers/pcmcia/Config.in --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/Config.in Sat Nov 10 00:03:11 2001 +++ /usr/src/sh/7751/cvs/linux/drivers/pcmcia/Config.in Sat Sep 28 13:21:09 2002 @@ -23,6 +23,7 @@ if [ "$CONFIG_HD64465" = "y" ]; then dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA fi + dep_tristate ' H7751 host bridge support' CONFIG_SH7751_PCMCIA $CONFIG_PCMCIA fi endmenu diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/Makefile /usr/src/sh/7751/cvs/linux/drivers/pcmcia/Makefile --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/Makefile Mon Nov 12 19:39:01 2001 +++ /usr/src/sh/7751/cvs/linux/drivers/pcmcia/Makefile Sat Sep 28 13:20:24 2002 @@ -38,6 +38,9 @@ ifeq ($(CONFIG_HD64465_PCMCIA),y) obj-y += hd64465_ss.o endif + ifeq ($(CONFIG_SH7751_PCMCIA),y) + obj-y += sh7751.o + endif else ifeq ($(CONFIG_PCMCIA),m) obj-m := pcmcia_core.o ds.o @@ -52,6 +55,9 @@ endif ifeq ($(CONFIG_HD64465_PCMCIA),m) obj-m += hd64465_ss.o + endif + ifeq ($(CONFIG_SH7751_PCMCIA),y) + obj-y += sh7751.o endif ifeq ($(CONFIG_CARDBUS),y) obj-m += yenta_socket.o diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/sh7751.c /usr/src/sh/7751/cvs/linux/drivers/pcmcia/sh7751.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/drivers/pcmcia/sh7751.c Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/drivers/pcmcia/sh7751.c Sat Sep 28 13:33:08 2002 @@ -0,0 +1,1034 @@ +/* + * $Id: hd64465_ss.c,v 1.1.1.1 2001/10/15 20:45:05 mrbrown Exp $ + * + * Device driver for the PCMCIA controller module of the + * Hitachi HD64465 handheld companion chip. + * + * Note that the HD64465 provides a very thin PCMCIA host bridge + * layer, requiring a lot of the work of supporting cards to be + * performed by the processor. For example: mapping of card + * interrupts to processor IRQs is done by IRQ demuxing software; + * IO and memory mappings are fixed; setting voltages according + * to card Voltage Select pins etc is done in software. + * + * Note also that this driver uses only the simple, fixed, + * 16MB, 16-bit wide mappings to PCMCIA spaces defined by the + * HD64465. Larger mappings, smaller mappings, or mappings of + * different width to the same socket, are all possible only by + * involving the SH7750's MMU, which is considered unnecessary here. + * The downside is that it may be possible for some drivers to + * break because they need or expect 8-bit mappings. + * + * This driver currently supports only the following configuration: + * SH7750 CPU, HD64465, TPS2206 voltage control chip. + * + * by Greg Banks + * (c) 2000 PocketPenguins Inc + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "cs_internal.h" + +#define MODNAME "hd64465_ss" + +/* #define HD64465_DEBUG 1 */ + +#ifndef HD64465_DEBUG +#define HD64465_DEBUG 0 +#endif + +#if HD64465_DEBUG +#define DPRINTK(args...) printk(MODNAME ": " args) +#else +#define DPRINTK(args...) +#endif + +extern int hd64465_io_debug; + + +/*============================================================*/ + +#define HS_IO_MAP_SIZE (64*1024) + +typedef struct hs_socket_t +{ + u_int irq; + u_long mem_base; + u_long mem_length; + void (*handler)(void *info, u_int events); + void *handler_info; + u_int pending_events; + u_int ctrl_base; + socket_state_t state; + pccard_io_map io_maps[MAX_IO_WIN]; + pccard_mem_map mem_maps[MAX_WIN]; + struct vm_struct *io_vma; /* allocated kernel vm for mapping IO space */ +} hs_socket_t; + +#define HS_MAX_SOCKETS 2 +static hs_socket_t hs_sockets[HS_MAX_SOCKETS]; +static spinlock_t hs_pending_event_lock = SPIN_LOCK_UNLOCKED; + +/* Calculate socket number from ptr into hs_sockets[] */ +#define hs_sockno(sp) (sp - hs_sockets) + +static socket_cap_t hs_socket_cap = +{ + SS_CAP_PCCARD /* support 16 bit cards */ + |SS_CAP_STATIC_MAP /* mappings are fixed in host memory */ + , + 0xffde/*0xffff*/, /* IRQs mapped in s/w so can do any, really */ + HD64465_PCC_WINDOW, /* 16MB fixed window size */ + 0, /* no PCI support */ + 0, /* no CardBus support */ + 0 /* no bus operations needed */ +}; + +#define hs_in(sp, r) inb((sp)->ctrl_base + (r)) +#define hs_out(sp, v, r) outb(v, (sp)->ctrl_base + (r)) + + +/* translate a boolean value to a bit in a register */ +#define bool_to_regbit(sp, r, bi, bo) \ + do { \ + unsigned short v = hs_in(sp, r); \ + if (bo) \ + v |= (bi); \ + else \ + v &= ~(bi); \ + hs_out(sp, v, r); \ + } while(0) + +/* register offsets from HD64465_REG_PCC[01]ISR */ +#define ISR 0x0 +#define GCR 0x2 +#define CSCR 0x4 +#define CSCIER 0x6 +#define SCR 0x8 + + +/* Mask and values for CSCIER register */ +#define IER_MASK 0x80 +#define IER_ON 0x3f /* interrupts on */ +#define IER_OFF 0x00 /* interrupts off */ + +/*============================================================*/ + +#if HD64465_DEBUG > 10 + +static void cis_hex_dump(const unsigned char *x, int len) +{ + int i; + + for (i=0 ; istartup(irq); + return 0; +} + +static void hs_shutdown_irq(unsigned int irq) +{ + hs_socket_disable_ireq(hs_mapped_irq[irq].sock); + hs_mapped_irq[irq].old_handler->shutdown(irq); +} + +static void hs_enable_irq(unsigned int irq) +{ + hs_socket_enable_ireq(hs_mapped_irq[irq].sock); + hs_mapped_irq[irq].old_handler->enable(irq); +} + +static void hs_disable_irq(unsigned int irq) +{ + hs_socket_disable_ireq(hs_mapped_irq[irq].sock); + hs_mapped_irq[irq].old_handler->disable(irq); +} + +extern struct hw_interrupt_type no_irq_type; + +static void hs_mask_and_ack_irq(unsigned int irq) +{ + hs_socket_disable_ireq(hs_mapped_irq[irq].sock); + /* ack_none() spuriously complains about an unexpected IRQ */ + if (hs_mapped_irq[irq].old_handler != &no_irq_type) + hs_mapped_irq[irq].old_handler->ack(irq); +} + +static void hs_end_irq(unsigned int irq) +{ + hs_socket_enable_ireq(hs_mapped_irq[irq].sock); + hs_mapped_irq[irq].old_handler->end(irq); +} + + +static struct hw_interrupt_type hd64465_ss_irq_type = { + typename: "PCMCIA-IRQ", + startup: hs_startup_irq, + shutdown: hs_shutdown_irq, + enable: hs_enable_irq, + disable: hs_disable_irq, + ack: hs_mask_and_ack_irq, + end: hs_end_irq +}; + +/* + * This function should only ever be called with interrupts disabled. + */ +static void hs_map_irq(hs_socket_t *sp, unsigned int irq) +{ + DPRINTK("hs_map_irq(sock=%d irq=%d)\n", hs_sockno(sp), irq); + + if (irq >= HS_NUM_MAPPED_IRQS) + return; + + hs_mapped_irq[irq].sock = sp; + /* insert ourselves as the irq controller */ + hs_mapped_irq[irq].old_handler = irq_desc[irq].handler; + irq_desc[irq].handler = &hd64465_ss_irq_type; +} + + +/* + * This function should only ever be called with interrupts disabled. + */ +static void hs_unmap_irq(hs_socket_t *sp, unsigned int irq) +{ + DPRINTK("hs_unmap_irq(sock=%d irq=%d)\n", hs_sockno(sp), irq); + + if (irq >= HS_NUM_MAPPED_IRQS) + return; + + /* restore the original irq controller */ + irq_desc[irq].handler = hs_mapped_irq[irq].old_handler; +} + +/*============================================================*/ + + +/* + * Set Vpp and Vcc (in tenths of a Volt). Does not + * support the hi-Z state. + * + * Note, this assumes the board uses a TPS2206 chip to control + * the Vcc and Vpp voltages to the hs_sockets. If your board + * uses the MIC2563 (also supported by the HD64465) then you + * will have to modify this function. + */ + /* 0V 3.3V 5.5V */ + +static int hs_set_voltages(hs_socket_t *sp, int Vcc, int Vpp) +{ + + return 1; +} + + +/*============================================================*/ + +/* + * Drive the RESET line to the card. + */ +static void hs_reset_socket(hs_socket_t *sp, int on) +{ +} + +/*============================================================*/ + +static int hs_init(unsigned int sock) +{ + hs_socket_t *sp = &hs_sockets[sock]; + + DPRINTK("hs_init(%d)\n", sock); + + sp->pending_events = 0; + sp->state.Vcc = 0; + sp->state.Vpp = 0; + hs_set_voltages(sp, 0, 0); + + return 0; +} + +/*============================================================*/ + +static int hs_suspend(unsigned int sock) +{ + DPRINTK("hs_suspend(%d)\n", sock); + + /* TODO */ + + return 0; +} + +/*============================================================*/ + +static int hs_register_callback(unsigned int sock, + void (*handler)(void *, unsigned int), void * info) +{ + hs_socket_t *sp = &hs_sockets[sock]; + + DPRINTK("hs_register_callback(%d)\n", sock); + sp->handler = handler; + sp->handler_info = info; + if (handler == 0) { + MOD_DEC_USE_COUNT; + } else { + MOD_INC_USE_COUNT; + } + return 0; +} + +/*============================================================*/ + +static int hs_inquire_socket(unsigned int sock, socket_cap_t *cap) +{ + DPRINTK("hs_inquire_socket(%d)\n", sock); + + *cap = hs_socket_cap; + return 0; +} + +/*============================================================*/ + +static int hs_get_status(unsigned int sock, u_int *value) +{ + hs_socket_t *sp = &hs_sockets[sock]; + unsigned int isr; + u_int status = 0; + + + isr = hs_in(sp, ISR); + + /* Card is seated and powered when *both* CD pins are low */ + if ((isr & HD64465_PCCISR_PCD_MASK) == 0) + { + status |= SS_DETECT; /* card present */ + + switch (isr & HD64465_PCCISR_PBVD_MASK) + { + case HD64465_PCCISR_PBVD_BATGOOD: + break; + case HD64465_PCCISR_PBVD_BATWARN: + status |= SS_BATWARN; + break; + default: + status |= SS_BATDEAD; + break; + } + + if (isr & HD64465_PCCISR_PREADY) + status |= SS_READY; + + if (isr & HD64465_PCCISR_PMWP) + status |= SS_WRPROT; + + /* Voltage Select pins interpreted as per Table 4-5 of the std. + * Assuming we have the TPS2206, the socket is a "Low Voltage + * key, 3.3V and 5V available, no X.XV available". + */ + switch (isr & (HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1)) + { + case HD64465_PCCISR_PVS1: + printk(KERN_NOTICE MODNAME ": cannot handle X.XV card, ignored\n"); + status = 0; + break; + case 0: + case HD64465_PCCISR_PVS2: + /* 3.3V */ + status |= SS_3VCARD; + break; + case HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1: + /* 5V */ + break; + } + + /* TODO: SS_POWERON */ + /* TODO: SS_STSCHG */ + } + + DPRINTK("hs_get_status(%d) = %x\n", sock, status); + + *value = status; + return 0; +} + +/*============================================================*/ + +static int hs_get_socket(unsigned int sock, socket_state_t *state) +{ + hs_socket_t *sp = &hs_sockets[sock]; + + DPRINTK("hs_get_socket(%d)\n", sock); + + *state = sp->state; + return 0; +} + +/*============================================================*/ + +static int hs_set_socket(unsigned int sock, socket_state_t *state) +{ + hs_socket_t *sp = &hs_sockets[sock]; + u_long flags; + u_int changed; + unsigned short cscier; + + DPRINTK("hs_set_socket(sock=%d, flags=%x, csc_mask=%x, Vcc=%d, Vpp=%d, io_irq=%d)\n", + sock, state->flags, state->csc_mask, state->Vcc, state->Vpp, state->io_irq); + + save_and_cli(flags); /* Don't want interrupts happening here */ + + if (state->Vpp != sp->state.Vpp || + state->Vcc != sp->state.Vcc) { + if (!hs_set_voltages(sp, state->Vcc, state->Vpp)) { + restore_flags(flags); + return -EINVAL; + } + } + +/* hd64465_io_debug = 1; */ + /* + * Handle changes in the Card Status Change mask, + * by propagating to the CSCR register + */ + changed = sp->state.csc_mask ^ state->csc_mask; + cscier = hs_in(sp, CSCIER); + + if (changed & SS_DETECT) { + if (state->csc_mask & SS_DETECT) + cscier |= HD64465_PCCCSCIER_PCDE; + else + cscier &= ~HD64465_PCCCSCIER_PCDE; + } + + if (changed & SS_READY) { + if (state->csc_mask & SS_READY) + cscier |= HD64465_PCCCSCIER_PRE; + else + cscier &= ~HD64465_PCCCSCIER_PRE; + } + + if (changed & SS_BATDEAD) { + if (state->csc_mask & SS_BATDEAD) + cscier |= HD64465_PCCCSCIER_PBDE; + else + cscier &= ~HD64465_PCCCSCIER_PBDE; + } + + if (changed & SS_BATWARN) { + if (state->csc_mask & SS_BATWARN) + cscier |= HD64465_PCCCSCIER_PBWE; + else + cscier &= ~HD64465_PCCCSCIER_PBWE; + } + + if (changed & SS_STSCHG) { + if (state->csc_mask & SS_STSCHG) + cscier |= HD64465_PCCCSCIER_PSCE; + else + cscier &= ~HD64465_PCCCSCIER_PSCE; + } + + hs_out(sp, cscier, CSCIER); + + if (sp->state.io_irq && !state->io_irq) + hs_unmap_irq(sp, sp->state.io_irq); + else if (!sp->state.io_irq && state->io_irq) + hs_map_irq(sp, state->io_irq); + + + /* + * Handle changes in the flags field, + * by propagating to config registers. + */ + changed = sp->state.flags ^ state->flags; + + if (changed & SS_IOCARD) { + DPRINTK("card type: %s\n", + (state->flags & SS_IOCARD ? "i/o" : "memory" )); + bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCT, + state->flags & SS_IOCARD); + } + + if (changed & SS_RESET) { + DPRINTK("%s reset card\n", + (state->flags & SS_RESET ? "start" : "stop")); + bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCR, + state->flags & SS_RESET); + } + + if (changed & SS_OUTPUT_ENA) { + DPRINTK("%sabling card output\n", + (state->flags & SS_OUTPUT_ENA ? "en" : "dis")); + bool_to_regbit(sp, GCR, HD64465_PCCGCR_PDRV, + state->flags & SS_OUTPUT_ENA); + } + + /* TODO: SS_SPKR_ENA */ + +/* hd64465_io_debug = 0; */ + sp->state = *state; + + restore_flags(flags); + +#if HD64465_DEBUG > 10 + if (state->flags & SS_OUTPUT_ENA) + cis_hex_dump((const unsigned char*)sp->mem_base, 0x100); +#endif + return 0; +} + +/*============================================================*/ + +static int hs_get_io_map(unsigned int sock, struct pccard_io_map *io) +{ + hs_socket_t *sp = &hs_sockets[sock]; + int map = io->map; + + DPRINTK("hs_get_io_map(%d, %d)\n", sock, map); + if (map >= MAX_IO_WIN) + return -EINVAL; + + *io = sp->io_maps[map]; + return 0; +} + +/*============================================================*/ + +static int hs_set_io_map(unsigned int sock, struct pccard_io_map *io) +{ + hs_socket_t *sp = &hs_sockets[sock]; + int map = io->map; + struct pccard_io_map *sio; + pgprot_t prot; + + DPRINTK("hs_set_io_map(sock=%d, map=%d, flags=0x%x, speed=%dns, start=0x%04x, stop=0x%04x)\n", + sock, map, io->flags, io->speed, io->start, io->stop); + if (map >= MAX_IO_WIN) + return -EINVAL; + sio = &sp->io_maps[map]; + + /* check for null changes */ + if (io->flags == sio->flags && + io->start == sio->start && + io->stop == sio->stop) + return 0; + + if (io->flags & MAP_AUTOSZ) + prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IODYN); + else if (io->flags & MAP_16BIT) + prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO16); + else + prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO8); + + /* TODO: handle MAP_USE_WAIT */ + if (io->flags & MAP_USE_WAIT) + printk(KERN_INFO MODNAME ": MAP_USE_WAIT unimplemented\n"); + /* TODO: handle MAP_PREFETCH */ + if (io->flags & MAP_PREFETCH) + printk(KERN_INFO MODNAME ": MAP_PREFETCH unimplemented\n"); + /* TODO: handle MAP_WRPROT */ + if (io->flags & MAP_WRPROT) + printk(KERN_INFO MODNAME ": MAP_WRPROT unimplemented\n"); + /* TODO: handle MAP_0WS */ + if (io->flags & MAP_0WS) + printk(KERN_INFO MODNAME ": MAP_0WS unimplemented\n"); + + if (io->flags & MAP_ACTIVE) { + unsigned long pstart, psize, paddrbase, vaddrbase; + + paddrbase = virt_to_phys((void*)(sp->mem_base + 2 * HD64465_PCC_WINDOW)); + vaddrbase = (unsigned long)sp->io_vma->addr; + pstart = io->start & PAGE_MASK; + psize = ((io->stop + PAGE_SIZE) & PAGE_MASK) - pstart; + + /* + * Change PTEs in only that portion of the mapping requested + * by the caller. This means that most of the time, most of + * the PTEs in the io_vma will be unmapped and only the bottom + * page will be mapped. But the code allows for weird cards + * that might want IO ports > 4K. + */ + DPRINTK("remap_page_range(vaddr=0x%08lx, paddr=0x%08lx, size=0x%08lxx)\n", + vaddrbase + pstart, paddrbase + pstart, psize); + remap_page_range(vaddrbase + pstart, paddrbase + pstart, psize, prot); + + /* + * Change the mapping used by inb() outb() etc + */ + } else { + } + + *sio = *io; + return 0; +} + +/*============================================================*/ + +static int hs_get_mem_map(unsigned int sock, struct pccard_mem_map *mem) +{ + hs_socket_t *sp = &hs_sockets[sock]; + int map = mem->map; + + DPRINTK("hs_get_mem_map(%d, %d)\n", sock, map); + if (map >= MAX_WIN) + return -EINVAL; + + *mem = sp->mem_maps[map]; + return 0; +} + +/*============================================================*/ + +static int hs_set_mem_map(unsigned int sock, struct pccard_mem_map *mem) +{ + hs_socket_t *sp = &hs_sockets[sock]; + struct pccard_mem_map *smem; + int map = mem->map; + unsigned long paddr, size; + +#if 0 + DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, sys_start=0x%08lx, sys_end=0x%08lx, card_start=0x%08x)\n", + sock, map, mem->flags, mem->sys_start, mem->sys_stop, mem->card_start); +#endif + + if (map >= MAX_WIN) + return -EINVAL; + smem = &sp->mem_maps[map]; + + size = mem->sys_stop - mem->sys_start + 1; + + paddr = sp->mem_base; /* base of Attribute mapping */ + if (!(mem->flags & MAP_ATTRIB)) + paddr += HD64465_PCC_WINDOW; /* base of Common mapping */ + paddr += mem->card_start; + + /* Because we specified SS_CAP_STATIC_MAP, we are obliged + * at this time to report the system address corresponding + * to the card address requested. This is how Socket Services + * queries our fixed mapping. I wish this fact had been + * documented - Greg Banks. + */ + mem->sys_start = paddr; + mem->sys_stop = paddr + size - 1; + + *smem = *mem; + + return 0; +} + +/* TODO: do we need to use the MMU to access Common memory ??? */ + +/*============================================================*/ + +static void hs_proc_setup(unsigned int sock, struct proc_dir_entry *base) +{ + DPRINTK("hs_proc_setup(%d)\n", sock); +} + +/*============================================================*/ + +/* + * This function is registered with the HD64465 glue code to do a + * secondary demux step on the PCMCIA interrupts. It handles + * mapping the IREQ request from the card to a standard Linux + * IRQ, as requested by SocketServices. + */ +static int hs_irq_demux(int irq, void *dev) +{ + hs_socket_t *sp = (hs_socket_t *)dev; + u_int cscr; + + DPRINTK("hs_irq_demux(irq=%d)\n", irq); + + if (sp->state.io_irq && + (cscr = hs_in(sp, CSCR)) & HD64465_PCCCSCR_PIREQ) { + cscr &= ~HD64465_PCCCSCR_PIREQ; + hs_out(sp, cscr, CSCR); + return sp->state.io_irq; + } + + return irq; +} + +/*============================================================*/ + +/* + * Interrupt handling routine. + * + * This uses the schedule_task() technique to cause reportable events + * such as card insertion and removal to be handled in keventd's + * process context. + */ + + +static void hs_events_bh(void *dummy) +{ + hs_socket_t *sp; + u_int events; + int i; + + for (i=0; ipending_events; + sp->pending_events = 0; + spin_unlock_irq(&hs_pending_event_lock); + + if (sp->handler) + sp->handler(sp->handler_info, events); + } +} + +static struct tq_struct hs_events_task = { + routine: hs_events_bh +}; + +static void hs_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + hs_socket_t *sp = (hs_socket_t *)dev; + u_int events = 0; + u_int cscr; + + + cscr = hs_in(sp, CSCR); + + DPRINTK("hs_interrupt, cscr=%04x\n", cscr); + + /* check for bus-related changes to be reported to Socket Services */ + if (cscr & HD64465_PCCCSCR_PCDC) { + /* double-check for a 16-bit card, as we don't support CardBus */ + if ((hs_in(sp, ISR) & HD64465_PCCISR_PCD_MASK) != 0) { + printk(KERN_NOTICE MODNAME + ": socket %d, card not a supported card type or not inserted correctly\n", + hs_sockno(sp)); + /* Don't do the rest unless a card is present */ + cscr &= ~(HD64465_PCCCSCR_PCDC| + HD64465_PCCCSCR_PRC| + HD64465_PCCCSCR_PBW| + HD64465_PCCCSCR_PBD| + HD64465_PCCCSCR_PSC); + } else { + cscr &= ~HD64465_PCCCSCR_PCDC; + events |= SS_DETECT; /* card insertion or removal */ + } + } + if (cscr & HD64465_PCCCSCR_PRC) { + cscr &= ~HD64465_PCCCSCR_PRC; + events |= SS_READY; /* ready signal changed */ + } + if (cscr & HD64465_PCCCSCR_PBW) { + cscr &= ~HD64465_PCCCSCR_PSC; + events |= SS_BATWARN; /* battery warning */ + } + if (cscr & HD64465_PCCCSCR_PBD) { + cscr &= ~HD64465_PCCCSCR_PSC; + events |= SS_BATDEAD; /* battery dead */ + } + if (cscr & HD64465_PCCCSCR_PSC) { + cscr &= ~HD64465_PCCCSCR_PSC; + events |= SS_STSCHG; /* STSCHG (status changed) signal */ + } + + if (cscr & HD64465_PCCCSCR_PIREQ) { + cscr &= ~HD64465_PCCCSCR_PIREQ; + + /* This should have been dealt with during irq demux */ + printk(KERN_NOTICE MODNAME ": unexpected IREQ from card\n"); + } + + hs_out(sp, cscr, CSCR); + + if (events) { + /* + * Arrange for events to be reported to the registered + * event handler function (from CardServices) in a process + * context (keventd) "soon". + */ + spin_lock(&hs_pending_event_lock); + sp->pending_events |= events; + spin_unlock(&hs_pending_event_lock); + + schedule_task(&hs_events_task); + } +} + +/*============================================================*/ + +static struct pccard_operations hs_operations = { + hs_init, + hs_suspend, + hs_register_callback, + hs_inquire_socket, + hs_get_status, + hs_get_socket, + hs_set_socket, + hs_get_io_map, + hs_set_io_map, + hs_get_mem_map, + hs_set_mem_map, + hs_proc_setup +}; + +static int hs_init_socket(hs_socket_t *sp, int irq, unsigned long mem_base, + unsigned int ctrl_base) +{ + unsigned short v; + int i, err; + + memset(sp, 0, sizeof(*sp)); + sp->irq = irq; + sp->mem_base = mem_base; + sp->mem_length = 4*HD64465_PCC_WINDOW; /* 16MB */ + sp->ctrl_base = ctrl_base; + + for (i=0 ; iio_maps[i].map = i; + for (i=0 ; imem_maps[i].map = i; + + if ((sp->io_vma = get_vm_area(HS_IO_MAP_SIZE, VM_IOREMAP)) == 0) + return -ENOMEM; + +// hd64465_register_irq_demux(sp->irq, hs_irq_demux, sp); + + if ((err = request_irq(sp->irq, hs_interrupt, SA_INTERRUPT, MODNAME, sp)) < 0) + return err; + if (request_mem_region(sp->mem_base, sp->mem_length, MODNAME) == 0) { + sp->mem_base = 0; + return -ENOMEM; + } + + + /* According to section 3.2 of the PCMCIA standard, low-voltage + * capable cards must implement cold insertion, i.e. Vpp and + * Vcc set to 0 before card is inserted. + */ + /*hs_set_voltages(sp, 0, 0);*/ + + /* hi-Z the outputs to the card and set 16MB map mode */ + v = hs_in(sp, GCR); + v &= ~HD64465_PCCGCR_PCCT; /* memory-only card */ + hs_out(sp, v, GCR); + + v = hs_in(sp, GCR); + v |= HD64465_PCCGCR_PDRV; /* enable outputs to card */ + hs_out(sp, v, GCR); + + v = hs_in(sp, GCR); + v |= HD64465_PCCGCR_PMMOD; /* 16MB mapping mode */ + hs_out(sp, v, GCR); + + v = hs_in(sp, GCR); + /* lowest 16MB of Common */ + v &= ~(HD64465_PCCGCR_PPA25|HD64465_PCCGCR_PPA24); + hs_out(sp, v, GCR); + + hs_reset_socket(sp, 1); + + return 0; +} + +static void hs_exit_socket(hs_socket_t *sp) +{ + unsigned short cscier, gcr; + + /* turn off interrupts in hardware */ + cscier = hs_in(sp, CSCIER); + cscier = (cscier & IER_MASK) | IER_OFF; + hs_out(sp, cscier, CSCIER); + + /* hi-Z the outputs to the card */ + gcr = hs_in(sp, GCR); + gcr &= HD64465_PCCGCR_PDRV; + hs_out(sp, gcr, GCR); + + /* power the card down */ + hs_set_voltages(sp, 0, 0); + + if (sp->mem_base != 0) + release_mem_region(sp->mem_base, sp->mem_length); + if (sp->irq != 0) { + free_irq(sp->irq, hs_interrupt); +// hd64465_unregister_irq_demux(sp->irq); + } + if (sp->io_vma != 0) + vfree(sp->io_vma->addr); +} + + +static int __init init_hs(void) +{ + servinfo_t serv; + int i; + unsigned short v; + + /* + * Check API version + */ + pcmcia_get_card_services_info(&serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE MODNAME ": Card Services release does not match!\n"); + return -ENODEV; + } + +/* hd64465_io_debug = 1; */ + + /* Wake both sockets out of STANDBY mode */ + /* TODO: wait 15ms */ + v = inw(HD64465_REG_SMSCR); + v &= ~(HD64465_SMSCR_PC0ST|HD64465_SMSCR_PC1ST); + outw(v, HD64465_REG_SMSCR); + + /* keep power controller out of shutdown mode */ + v = inb(HD64465_REG_PCC0SCR); + v |= HD64465_PCCSCR_SHDN; + outb(v, HD64465_REG_PCC0SCR); + + /* use serial (TPS2206) power controller */ + v = inb(HD64465_REG_PCC0CSCR); + v |= HD64465_PCCCSCR_PSWSEL; + outb(v, HD64465_REG_PCC0CSCR); + + hs_set_voltages(&hs_sockets[0], 0, 0); + hs_set_voltages(&hs_sockets[1], 0, 0); + + /* + * Setup hs_sockets[] structures and request system resources. + * TODO: on memory allocation failure, power down the socket + * before quitting. + */ +/* + i = hs_init_socket(&hs_sockets[0], + HD64465_IRQ_PCMCIA0, + HD64465_PCC0_BASE, + HD64465_REG_PCC0ISR); + if (i < 0) + return i; + i = hs_init_socket(&hs_sockets[1], + HD64465_IRQ_PCMCIA1, + HD64465_PCC1_BASE, + HD64465_REG_PCC1ISR); + if (i < 0) + return i; +*/ +/* hd64465_io_debug = 0; */ + + + if (register_ss_entry(HS_MAX_SOCKETS, &hs_operations) != 0) { + for (i=0 ; imem_base, sp->irq, + sp->io_vma->size>>10, (unsigned long)sp->io_vma->addr); + } + + return 0; +} + +static void __exit exit_hs(void) +{ + u_long flags; + int i; + + save_and_cli(flags); + + /* + * Release kernel resources + */ + for (i=0 ; i # elif defined(CONFIG_SH_ADX) # include +# elif defined(CONFIG_SH_FLEXBOX) +# include # elif defined(CONFIG_SH_UNKNOWN) # include # else diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/io_flexbox.h /usr/src/sh/7751/cvs/linux/include/asm-sh/io_flexbox.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/io_flexbox.h Thu Jan 1 02:00:00 1970 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/io_flexbox.h Fri Jun 28 12:19:15 2002 @@ -0,0 +1,89 @@ +/* + * include/asm-sh/io_unknown.h + * + * Copyright 2000 Stuart Menefy (stuart.menefy@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * IO functions for use when we don't know what machine we are on + */ + +#ifndef _ASM_SH_IO_FLEXBOX_H +#define _ASM_SH_IO_FLEXBOX_H + +extern unsigned char flexbox_inb(unsigned long port); +extern unsigned short flexbox_inw(unsigned long port); +extern unsigned int flexbox_inl(unsigned long port); + +extern void flexbox_outb(unsigned char value, unsigned long port); +extern void flexbox_outw(unsigned short value, unsigned long port); +extern void flexbox_outl(unsigned int value, unsigned long port); + +extern unsigned char flexbox_inb_p(unsigned long port); +extern unsigned short flexbox_inw_p(unsigned long port); +extern unsigned int flexbox_inl_p(unsigned long port); +extern void flexbox_outb_p(unsigned char value, unsigned long port); +extern void flexbox_outw_p(unsigned short value, unsigned long port); +extern void flexbox_outl_p(unsigned int value, unsigned long port); + +extern void flexbox_insb(unsigned long port, void *addr, unsigned long count); +extern void flexbox_insw(unsigned long port, void *addr, unsigned long count); +extern void flexbox_insl(unsigned long port, void *addr, unsigned long count); +extern void flexbox_outsb(unsigned long port, const void *addr, unsigned long count); +extern void flexbox_outsw(unsigned long port, const void *addr, unsigned long count); +extern void flexbox_outsl(unsigned long port, const void *addr, unsigned long count); + +extern unsigned char flexbox_readb(unsigned long addr); +extern unsigned short flexbox_readw(unsigned long addr); +extern unsigned int flexbox_readl(unsigned long addr); +extern void flexbox_writeb(unsigned char b, unsigned long addr); +extern void flexbox_writew(unsigned short b, unsigned long addr); +extern void flexbox_writel(unsigned int b, unsigned long addr); + +extern unsigned long flexbox_isa_port2addr(unsigned long offset); +extern void *flexbox_ioremap(unsigned long offset, unsigned long size); +extern void flexbox_iounmap(void *addr); +extern int init_flexbox_IRQ(void); + +extern int flex_out; +extern int flex_rev; +extern void set_cs(int); + +#ifdef __WANT_IO_DEF + +# define __inb flexbox_inb +# define __inw flexbox_inw +# define __inl flexbox_inl +# define __outb flexbox_outb +# define __outw flexbox_outw +# define __outl flexbox_outl + +# define __inb_p flexbox_inb_p +# define __inw_p flexbox_inw_p +# define __inl_p flexbox_inl_p +# define __outb_p flexbox_outb_p +# define __outw_p flexbox_outw_p +# define __outl_p flexbox_outl_p + +# define __insb flexbox_insb +# define __insw flexbox_insw +# define __insl flexbox_insl +# define __outsb flexbox_outsb +# define __outsw flexbox_outsw +# define __outsl flexbox_outsl + +# define __readb flexbox_readb +# define __readw flexbox_readw +# define __readl flexbox_readl +# define __writeb flexbox_writeb +# define __writew flexbox_writew +# define __writel flexbox_writel + +# define __isa_port2addr flexbox_isa_port2addr +# define __ioremap flexbox_ioremap +# define __iounmap flexbox_iounmap + +#endif + +#endif /* _ASM_SH_IO_FLEXBOX_H */ diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/irq.h /usr/src/sh/7751/cvs/linux/include/asm-sh/irq.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/irq.h Fri May 17 15:50:46 2002 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/irq.h Sat Jun 22 20:51:52 2002 @@ -21,6 +21,7 @@ #define INTC_IPRA 0xffd00004UL #define INTC_IPRB 0xffd00008UL #define INTC_IPRC 0xffd0000cUL +#define INTC_IPRD 0xffd00010UL #endif #define TIMER_IRQ 16 diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/machvec.h /usr/src/sh/7751/cvs/linux/include/asm-sh/machvec.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/machvec.h Fri May 17 15:50:45 2002 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/machvec.h Sat Jun 22 20:51:52 2002 @@ -96,6 +96,7 @@ #define MACH_BIGSUR (sh_mv.mv_hw_bigsur) #define MACH_7751SE (sh_mv.mv_hw_7751se) #define MACH_ADX (sh_mv.mv_hw_adx) +#define MACH_FLEXBOX (sh_mv.mv_hw_flexbox) #else # ifdef CONFIG_SH_SOLUTION_ENGINE # define MACH_SE 1 @@ -161,6 +162,11 @@ # define MACH_ADX 1 # else # define MACH_ADX 0 +# endif +# ifdef CONFIG_SH_FLEXBOX +# define MACH_FLEXBOX 1 +# else +# define MACH_FLEXBOX 0 # endif #endif diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/mc146818rtc.h /usr/src/sh/7751/cvs/linux/include/asm-sh/mc146818rtc.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/mc146818rtc.h Fri May 17 15:51:15 2002 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/mc146818rtc.h Mon Jun 24 17:12:51 2002 @@ -12,7 +12,11 @@ #undef RTC_IRQ #define RTC_IRQ 0 -#if defined(__sh3__) +#ifdef CONFIG_SH_FLEXBOX +#define RTC_PORT(n) (R64CNT+(n)*4) +extern int CMOS_READ(int addr); +extern int CMOS_WRITE(int val, int addr); +#elif defined(__sh3__) #define RTC_PORT(n) (R64CNT+(n)*2) #define CMOS_READ(addr) __CMOS_READ(addr,b) #define CMOS_WRITE(val,addr) __CMOS_WRITE(val,addr,b) @@ -156,4 +160,5 @@ break; \ } \ }) + #endif /* _ASM_MC146818RTC_H */ diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/pci-sh7751.h /usr/src/sh/7751/cvs/linux/include/asm-sh/pci-sh7751.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/pci-sh7751.h Mon Oct 15 22:45:11 2001 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/pci-sh7751.h Sat Oct 5 17:34:09 2002 @@ -36,6 +36,7 @@ /* Platform Specific Values */ #define SH7751_VENDOR_ID 0x1054 #define SH7751_DEVICE_ID 0x3505 +#define SH7751R_DEVICE_ID 0x350e /* SH7751 Specific Values */ #define SH7751_PCI_CONFIG_BASE 0xFD000000 /* Config space base addr */ diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/pci.h /usr/src/sh/7751/cvs/linux/include/asm-sh/pci.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/asm-sh/pci.h Fri May 17 15:51:37 2002 +++ /usr/src/sh/7751/cvs/linux/include/asm-sh/pci.h Fri Jun 28 12:19:18 2002 @@ -29,6 +29,9 @@ #elif defined(CONFIG_SH_7751_SOLUTION_ENGINE) #define PCIBIOS_MIN_IO 0x4000 #define PCIBIOS_MIN_MEM 0xFD000000 +#else +#define PCIBIOS_MIN_IO 0x4000 +#define PCIBIOS_MIN_MEM 0xFD000000 #endif struct pci_dev; diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/linux/sysctl.h /usr/src/sh/7751/cvs/linux/include/linux/sysctl.h --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/include/linux/sysctl.h Fri May 17 15:50:48 2002 +++ /usr/src/sh/7751/cvs/linux/include/linux/sysctl.h Sun Jun 23 15:56:39 2002 @@ -124,6 +124,7 @@ KERN_CORE_USES_PID=52, /* int: use core or core.%pid */ KERN_TAINTED=53, /* int: various kernel tainted flags */ KERN_CADPID=54, /* int: PID of the process to notify on CAD */ + KERN_SH_FLEXBOX_LEDS=55, }; diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/kernel/printk.c /usr/src/sh/7751/cvs/linux/kernel/printk.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/kernel/printk.c Mon Feb 25 21:38:13 2002 +++ /usr/src/sh/7751/cvs/linux/kernel/printk.c Sun Jun 23 11:59:15 2002 @@ -412,7 +412,30 @@ char *p; static char printk_buf[1024]; static int log_level_unknown = 1; +#if 0 +#define TDFE 0x20 +{ + char *SCFTDR2, *s; + volatile unsigned short *SCFSR2; + va_start(args, fmt); + printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); + va_end(args); + s=printk_buf; + + while (*s) { + + SCFTDR2 = (char *)0xffe8000c; + SCFSR2 = (unsigned short *)0xffe80010; + + while ( ((*SCFSR2) & TDFE) == 0 ); + + *SCFTDR2 = *s; + *SCFSR2 = ~TDFE; + s++; + } +} +#endif if (oops_in_progress) { /* If a crash is occurring, make sure we can't deadlock */ spin_lock_init(&logbuf_lock); diff -urN --exclude-from=diff.exc /usr/src/sh/7751/cvs/linux-2.4.18.sh/kernel/sysctl.c /usr/src/sh/7751/cvs/linux/kernel/sysctl.c --- /usr/src/sh/7751/cvs/linux-2.4.18.sh/kernel/sysctl.c Fri Dec 21 19:42:04 2001 +++ /usr/src/sh/7751/cvs/linux/kernel/sysctl.c Sat Jun 29 21:09:18 2002 @@ -39,6 +39,11 @@ #if defined(CONFIG_SYSCTL) +extern int flex_out; +int flex_leds=0; +int proc_doleds(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp); + /* External variables not in a header file. */ extern int panic_timeout; extern int C_A_D; @@ -256,6 +261,10 @@ {KERN_S390_USER_DEBUG_LOGGING,"userprocess_debug", &sysctl_userprocess_debug,sizeof(int),0644,NULL,&proc_dointvec}, #endif +#ifdef CONFIG_SH_FLEXBOX + {KERN_SH_FLEXBOX_LEDS, "leds", &flex_leds, sizeof(int), + 0644, NULL, &proc_doleds}, +#endif {0} }; @@ -933,6 +942,41 @@ { return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); } + +#ifdef CONFIG_SH_FLEXBOX +#ifdef CONFIG_CPU_SUBTYPE_SH7751 +int proc_doleds(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int i, k; + volatile unsigned char *ii; + volatile unsigned char j; + + i=do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); + k=flex_leds & 4; /* write protect */ + flex_leds &= 3; + flex_out &= ~0x1c0; + flex_out |= flex_leds<<7; + flex_out |= k<<4; + + ii=(volatile unsigned char *) (flex_out | 0xa4000000); + j=*ii; + + return i; +} +#else +int proc_doleds(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int i; + unsigned short *PTDRA; + i=do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); + PTDRA = (unsigned short *)0xff800030; + *PTDRA=0xffffff00 | ~flex_leds; + return i; +} +#endif +#endif /* * init may raise the set.