mirror of
https://github.com/torvalds/linux.git
synced 2024-12-21 02:21:36 +00:00
ad68bb9f7a
This is important because on PXA3xx, the physical mapping of SMEMC registers differs from the one on PXA2xx. In order to get PCMCIA working on both PXA2xx and PXA320, the PCMCIA driver was adjusted accordingly as well. Also, various places in the kernel had to be patched to use __raw_read/__raw_write. Signed-off-by: Marek Vasut <marek.vasut@gmail.com> Acked-by: Haojian Zhuang <haojian.zhuang@gmail.com> Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
531 lines
12 KiB
C
531 lines
12 KiB
C
/*
|
|
* linux/arch/arm/mach-pxa/cm-x2xx.c
|
|
*
|
|
* Copyright (C) 2008 CompuLab, Ltd.
|
|
* Mike Rapoport <mike@compulab.co.il>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sysdev.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/gpio.h>
|
|
|
|
#include <linux/dm9000.h>
|
|
#include <linux/leds.h>
|
|
|
|
#include <asm/mach/arch.h>
|
|
#include <asm/mach-types.h>
|
|
#include <asm/mach/map.h>
|
|
|
|
#include <mach/pxa2xx-regs.h>
|
|
#include <mach/audio.h>
|
|
#include <mach/pxafb.h>
|
|
#include <mach/smemc.h>
|
|
|
|
#include <asm/hardware/it8152.h>
|
|
|
|
#include "generic.h"
|
|
#include "cm-x2xx-pci.h"
|
|
|
|
extern void cmx255_init(void);
|
|
extern void cmx270_init(void);
|
|
|
|
/* reserve IRQs for IT8152 */
|
|
#define CMX2XX_NR_IRQS (IRQ_BOARD_START + 40)
|
|
|
|
/* virtual addresses for statically mapped regions */
|
|
#define CMX2XX_VIRT_BASE (0xe8000000)
|
|
#define CMX2XX_IT8152_VIRT (CMX2XX_VIRT_BASE)
|
|
|
|
/* physical address if local-bus attached devices */
|
|
#define CMX255_DM9000_PHYS_BASE (PXA_CS1_PHYS + (8 << 22))
|
|
#define CMX270_DM9000_PHYS_BASE (PXA_CS1_PHYS + (6 << 22))
|
|
|
|
/* leds */
|
|
#define CMX255_GPIO_RED (27)
|
|
#define CMX255_GPIO_GREEN (32)
|
|
#define CMX270_GPIO_RED (93)
|
|
#define CMX270_GPIO_GREEN (94)
|
|
|
|
/* GPIO IRQ usage */
|
|
#define GPIO22_ETHIRQ (22)
|
|
#define GPIO10_ETHIRQ (10)
|
|
#define CMX255_GPIO_IT8152_IRQ (0)
|
|
#define CMX270_GPIO_IT8152_IRQ (22)
|
|
|
|
#define CMX255_ETHIRQ IRQ_GPIO(GPIO22_ETHIRQ)
|
|
#define CMX270_ETHIRQ IRQ_GPIO(GPIO10_ETHIRQ)
|
|
|
|
#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
|
|
static struct resource cmx255_dm9000_resource[] = {
|
|
[0] = {
|
|
.start = CMX255_DM9000_PHYS_BASE,
|
|
.end = CMX255_DM9000_PHYS_BASE + 3,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
[1] = {
|
|
.start = CMX255_DM9000_PHYS_BASE + 4,
|
|
.end = CMX255_DM9000_PHYS_BASE + 4 + 500,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
[2] = {
|
|
.start = CMX255_ETHIRQ,
|
|
.end = CMX255_ETHIRQ,
|
|
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
|
|
}
|
|
};
|
|
|
|
static struct resource cmx270_dm9000_resource[] = {
|
|
[0] = {
|
|
.start = CMX270_DM9000_PHYS_BASE,
|
|
.end = CMX270_DM9000_PHYS_BASE + 3,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
[1] = {
|
|
.start = CMX270_DM9000_PHYS_BASE + 8,
|
|
.end = CMX270_DM9000_PHYS_BASE + 8 + 500,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
[2] = {
|
|
.start = CMX270_ETHIRQ,
|
|
.end = CMX270_ETHIRQ,
|
|
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
|
|
}
|
|
};
|
|
|
|
static struct dm9000_plat_data cmx270_dm9000_platdata = {
|
|
.flags = DM9000_PLATF_32BITONLY | DM9000_PLATF_NO_EEPROM,
|
|
};
|
|
|
|
static struct platform_device cmx2xx_dm9000_device = {
|
|
.name = "dm9000",
|
|
.id = 0,
|
|
.num_resources = ARRAY_SIZE(cmx270_dm9000_resource),
|
|
.dev = {
|
|
.platform_data = &cmx270_dm9000_platdata,
|
|
}
|
|
};
|
|
|
|
static void __init cmx2xx_init_dm9000(void)
|
|
{
|
|
if (cpu_is_pxa25x())
|
|
cmx2xx_dm9000_device.resource = cmx255_dm9000_resource;
|
|
else
|
|
cmx2xx_dm9000_device.resource = cmx270_dm9000_resource;
|
|
platform_device_register(&cmx2xx_dm9000_device);
|
|
}
|
|
#else
|
|
static inline void cmx2xx_init_dm9000(void) {}
|
|
#endif
|
|
|
|
/* UCB1400 touchscreen controller */
|
|
#if defined(CONFIG_TOUCHSCREEN_UCB1400) || defined(CONFIG_TOUCHSCREEN_UCB1400_MODULE)
|
|
static struct platform_device cmx2xx_ts_device = {
|
|
.name = "ucb1400_core",
|
|
.id = -1,
|
|
};
|
|
|
|
static void __init cmx2xx_init_touchscreen(void)
|
|
{
|
|
platform_device_register(&cmx2xx_ts_device);
|
|
}
|
|
#else
|
|
static inline void cmx2xx_init_touchscreen(void) {}
|
|
#endif
|
|
|
|
/* CM-X270 LEDs */
|
|
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
|
|
static struct gpio_led cmx2xx_leds[] = {
|
|
[0] = {
|
|
.name = "cm-x2xx:red",
|
|
.default_trigger = "nand-disk",
|
|
.active_low = 1,
|
|
},
|
|
[1] = {
|
|
.name = "cm-x2xx:green",
|
|
.default_trigger = "heartbeat",
|
|
.active_low = 1,
|
|
},
|
|
};
|
|
|
|
static struct gpio_led_platform_data cmx2xx_gpio_led_pdata = {
|
|
.num_leds = ARRAY_SIZE(cmx2xx_leds),
|
|
.leds = cmx2xx_leds,
|
|
};
|
|
|
|
static struct platform_device cmx2xx_led_device = {
|
|
.name = "leds-gpio",
|
|
.id = -1,
|
|
.dev = {
|
|
.platform_data = &cmx2xx_gpio_led_pdata,
|
|
},
|
|
};
|
|
|
|
static void __init cmx2xx_init_leds(void)
|
|
{
|
|
if (cpu_is_pxa25x()) {
|
|
cmx2xx_leds[0].gpio = CMX255_GPIO_RED;
|
|
cmx2xx_leds[1].gpio = CMX255_GPIO_GREEN;
|
|
} else {
|
|
cmx2xx_leds[0].gpio = CMX270_GPIO_RED;
|
|
cmx2xx_leds[1].gpio = CMX270_GPIO_GREEN;
|
|
}
|
|
platform_device_register(&cmx2xx_led_device);
|
|
}
|
|
#else
|
|
static inline void cmx2xx_init_leds(void) {}
|
|
#endif
|
|
|
|
#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
|
|
/*
|
|
Display definitions
|
|
keep these for backwards compatibility, although symbolic names (as
|
|
e.g. in lpd270.c) looks better
|
|
*/
|
|
#define MTYPE_STN320x240 0
|
|
#define MTYPE_TFT640x480 1
|
|
#define MTYPE_CRT640x480 2
|
|
#define MTYPE_CRT800x600 3
|
|
#define MTYPE_TFT320x240 6
|
|
#define MTYPE_STN640x480 7
|
|
|
|
static struct pxafb_mode_info generic_stn_320x240_mode = {
|
|
.pixclock = 76923,
|
|
.bpp = 8,
|
|
.xres = 320,
|
|
.yres = 240,
|
|
.hsync_len = 3,
|
|
.vsync_len = 2,
|
|
.left_margin = 3,
|
|
.upper_margin = 0,
|
|
.right_margin = 3,
|
|
.lower_margin = 0,
|
|
.sync = (FB_SYNC_HOR_HIGH_ACT |
|
|
FB_SYNC_VERT_HIGH_ACT),
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_stn_320x240 = {
|
|
.modes = &generic_stn_320x240_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_STN_8BPP | LCD_PCLK_EDGE_FALL |\
|
|
LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mode_info generic_tft_640x480_mode = {
|
|
.pixclock = 38461,
|
|
.bpp = 8,
|
|
.xres = 640,
|
|
.yres = 480,
|
|
.hsync_len = 60,
|
|
.vsync_len = 2,
|
|
.left_margin = 70,
|
|
.upper_margin = 10,
|
|
.right_margin = 70,
|
|
.lower_margin = 5,
|
|
.sync = 0,
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_tft_640x480 = {
|
|
.modes = &generic_tft_640x480_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_TFT_8BPP | LCD_PCLK_EDGE_FALL |\
|
|
LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mode_info generic_crt_640x480_mode = {
|
|
.pixclock = 38461,
|
|
.bpp = 8,
|
|
.xres = 640,
|
|
.yres = 480,
|
|
.hsync_len = 63,
|
|
.vsync_len = 2,
|
|
.left_margin = 81,
|
|
.upper_margin = 33,
|
|
.right_margin = 16,
|
|
.lower_margin = 10,
|
|
.sync = (FB_SYNC_HOR_HIGH_ACT |
|
|
FB_SYNC_VERT_HIGH_ACT),
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_crt_640x480 = {
|
|
.modes = &generic_crt_640x480_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_TFT_8BPP | LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mode_info generic_crt_800x600_mode = {
|
|
.pixclock = 28846,
|
|
.bpp = 8,
|
|
.xres = 800,
|
|
.yres = 600,
|
|
.hsync_len = 63,
|
|
.vsync_len = 2,
|
|
.left_margin = 26,
|
|
.upper_margin = 21,
|
|
.right_margin = 26,
|
|
.lower_margin = 11,
|
|
.sync = (FB_SYNC_HOR_HIGH_ACT |
|
|
FB_SYNC_VERT_HIGH_ACT),
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_crt_800x600 = {
|
|
.modes = &generic_crt_800x600_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_TFT_8BPP | LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mode_info generic_tft_320x240_mode = {
|
|
.pixclock = 134615,
|
|
.bpp = 16,
|
|
.xres = 320,
|
|
.yres = 240,
|
|
.hsync_len = 63,
|
|
.vsync_len = 7,
|
|
.left_margin = 75,
|
|
.upper_margin = 0,
|
|
.right_margin = 15,
|
|
.lower_margin = 15,
|
|
.sync = 0,
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_tft_320x240 = {
|
|
.modes = &generic_tft_320x240_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_TFT_16BPP | LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mode_info generic_stn_640x480_mode = {
|
|
.pixclock = 57692,
|
|
.bpp = 8,
|
|
.xres = 640,
|
|
.yres = 480,
|
|
.hsync_len = 4,
|
|
.vsync_len = 2,
|
|
.left_margin = 10,
|
|
.upper_margin = 5,
|
|
.right_margin = 10,
|
|
.lower_margin = 5,
|
|
.sync = (FB_SYNC_HOR_HIGH_ACT |
|
|
FB_SYNC_VERT_HIGH_ACT),
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info generic_stn_640x480 = {
|
|
.modes = &generic_stn_640x480_mode,
|
|
.num_modes = 1,
|
|
.lcd_conn = LCD_COLOR_STN_8BPP | LCD_AC_BIAS_FREQ(0xff),
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info *cmx2xx_display = &generic_crt_640x480;
|
|
|
|
static int __init cmx2xx_set_display(char *str)
|
|
{
|
|
int disp_type = simple_strtol(str, NULL, 0);
|
|
switch (disp_type) {
|
|
case MTYPE_STN320x240:
|
|
cmx2xx_display = &generic_stn_320x240;
|
|
break;
|
|
case MTYPE_TFT640x480:
|
|
cmx2xx_display = &generic_tft_640x480;
|
|
break;
|
|
case MTYPE_CRT640x480:
|
|
cmx2xx_display = &generic_crt_640x480;
|
|
break;
|
|
case MTYPE_CRT800x600:
|
|
cmx2xx_display = &generic_crt_800x600;
|
|
break;
|
|
case MTYPE_TFT320x240:
|
|
cmx2xx_display = &generic_tft_320x240;
|
|
break;
|
|
case MTYPE_STN640x480:
|
|
cmx2xx_display = &generic_stn_640x480;
|
|
break;
|
|
default: /* fallback to CRT 640x480 */
|
|
cmx2xx_display = &generic_crt_640x480;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
This should be done really early to get proper configuration for
|
|
frame buffer.
|
|
Indeed, pxafb parameters can be used istead, but CM-X2XX bootloader
|
|
has limitied line length for kernel command line, and also it will
|
|
break compatibitlty with proprietary releases already in field.
|
|
*/
|
|
__setup("monitor=", cmx2xx_set_display);
|
|
|
|
static void __init cmx2xx_init_display(void)
|
|
{
|
|
set_pxa_fb_info(cmx2xx_display);
|
|
}
|
|
#else
|
|
static inline void cmx2xx_init_display(void) {}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM
|
|
static unsigned long sleep_save_msc[10];
|
|
|
|
static int cmx2xx_suspend(struct sys_device *dev, pm_message_t state)
|
|
{
|
|
cmx2xx_pci_suspend();
|
|
|
|
/* save MSC registers */
|
|
sleep_save_msc[0] = __raw_readl(MSC0);
|
|
sleep_save_msc[1] = __raw_readl(MSC1);
|
|
sleep_save_msc[2] = __raw_readl(MSC2);
|
|
|
|
/* setup power saving mode registers */
|
|
PCFR = 0x0;
|
|
PSLR = 0xff400000;
|
|
PMCR = 0x00000005;
|
|
PWER = 0x80000000;
|
|
PFER = 0x00000000;
|
|
PRER = 0x00000000;
|
|
PGSR0 = 0xC0018800;
|
|
PGSR1 = 0x004F0002;
|
|
PGSR2 = 0x6021C000;
|
|
PGSR3 = 0x00020000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmx2xx_resume(struct sys_device *dev)
|
|
{
|
|
cmx2xx_pci_resume();
|
|
|
|
/* restore MSC registers */
|
|
__raw_writel(sleep_save_msc[0], MSC0);
|
|
__raw_writel(sleep_save_msc[1], MSC1);
|
|
__raw_writel(sleep_save_msc[2], MSC2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct sysdev_class cmx2xx_pm_sysclass = {
|
|
.name = "pm",
|
|
.resume = cmx2xx_resume,
|
|
.suspend = cmx2xx_suspend,
|
|
};
|
|
|
|
static struct sys_device cmx2xx_pm_device = {
|
|
.cls = &cmx2xx_pm_sysclass,
|
|
};
|
|
|
|
static int __init cmx2xx_pm_init(void)
|
|
{
|
|
int error;
|
|
error = sysdev_class_register(&cmx2xx_pm_sysclass);
|
|
if (error == 0)
|
|
error = sysdev_register(&cmx2xx_pm_device);
|
|
return error;
|
|
}
|
|
#else
|
|
static int __init cmx2xx_pm_init(void) { return 0; }
|
|
#endif
|
|
|
|
#if defined(CONFIG_SND_PXA2XX_AC97) || defined(CONFIG_SND_PXA2XX_AC97_MODULE)
|
|
static void __init cmx2xx_init_ac97(void)
|
|
{
|
|
pxa_set_ac97_info(NULL);
|
|
}
|
|
#else
|
|
static inline void cmx2xx_init_ac97(void) {}
|
|
#endif
|
|
|
|
static void __init cmx2xx_init(void)
|
|
{
|
|
pxa_set_ffuart_info(NULL);
|
|
pxa_set_btuart_info(NULL);
|
|
pxa_set_stuart_info(NULL);
|
|
|
|
cmx2xx_pm_init();
|
|
|
|
if (cpu_is_pxa25x())
|
|
cmx255_init();
|
|
else
|
|
cmx270_init();
|
|
|
|
cmx2xx_init_dm9000();
|
|
cmx2xx_init_display();
|
|
cmx2xx_init_ac97();
|
|
cmx2xx_init_touchscreen();
|
|
cmx2xx_init_leds();
|
|
}
|
|
|
|
static void __init cmx2xx_init_irq(void)
|
|
{
|
|
if (cpu_is_pxa25x()) {
|
|
pxa25x_init_irq();
|
|
cmx2xx_pci_init_irq(CMX255_GPIO_IT8152_IRQ);
|
|
} else {
|
|
pxa27x_init_irq();
|
|
cmx2xx_pci_init_irq(CMX270_GPIO_IT8152_IRQ);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PCI
|
|
/* Map PCI companion statically */
|
|
static struct map_desc cmx2xx_io_desc[] __initdata = {
|
|
[0] = { /* PCI bridge */
|
|
.virtual = CMX2XX_IT8152_VIRT,
|
|
.pfn = __phys_to_pfn(PXA_CS4_PHYS),
|
|
.length = SZ_64M,
|
|
.type = MT_DEVICE
|
|
},
|
|
};
|
|
|
|
static void __init cmx2xx_map_io(void)
|
|
{
|
|
if (cpu_is_pxa25x())
|
|
pxa25x_map_io();
|
|
|
|
if (cpu_is_pxa27x())
|
|
pxa27x_map_io();
|
|
|
|
iotable_init(cmx2xx_io_desc, ARRAY_SIZE(cmx2xx_io_desc));
|
|
|
|
it8152_base_address = CMX2XX_IT8152_VIRT;
|
|
}
|
|
#else
|
|
static void __init cmx2xx_map_io(void)
|
|
{
|
|
if (cpu_is_pxa25x())
|
|
pxa25x_map_io();
|
|
|
|
if (cpu_is_pxa27x())
|
|
pxa27x_map_io();
|
|
}
|
|
#endif
|
|
|
|
MACHINE_START(ARMCORE, "Compulab CM-X2XX")
|
|
.boot_params = 0xa0000100,
|
|
.map_io = cmx2xx_map_io,
|
|
.nr_irqs = CMX2XX_NR_IRQS,
|
|
.init_irq = cmx2xx_init_irq,
|
|
.timer = &pxa_timer,
|
|
.init_machine = cmx2xx_init,
|
|
MACHINE_END
|