Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
[sfrench/cifs-2.6.git] / arch / arm / mach-pxa / magician.c
index d98ef7ada2f8e38008a3cf05646425021d3fa601..d70be75bd199552d63cbc7d851d9fbd39f87d4c4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
+#include <linux/mfd/htc-egpio.h>
+#include <linux/mfd/htc-pasic3.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/physmap.h>
+#include <linux/pda_power.h>
 
 #include <asm/gpio.h>
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/arch/magician.h>
+#include <asm/arch/mfp-pxa27x.h>
 #include <asm/arch/pxa-regs.h>
 #include <asm/arch/pxafb.h>
+#include <asm/arch/i2c.h>
+#include <asm/arch/mmc.h>
 #include <asm/arch/irda.h>
 #include <asm/arch/ohci.h>
 
 #include "generic.h"
 
+static unsigned long magician_pin_config[] = {
+
+       /* SDRAM and Static Memory I/O Signals */
+       GPIO20_nSDCS_2,
+       GPIO21_nSDCS_3,
+       GPIO15_nCS_1,
+       GPIO78_nCS_2,   /* PASIC3 */
+       GPIO79_nCS_3,   /* EGPIO CPLD */
+       GPIO80_nCS_4,
+       GPIO33_nCS_5,
+
+       /* I2C */
+       GPIO117_I2C_SCL,
+       GPIO118_I2C_SDA,
+
+       /* PWM 0 */
+       GPIO16_PWM0_OUT,
+
+       /* I2S */
+       GPIO28_I2S_BITCLK_OUT,
+       GPIO29_I2S_SDATA_IN,
+       GPIO31_I2S_SYNC,
+       GPIO113_I2S_SYSCLK,
+
+       /* SSP 2 */
+       GPIO19_SSP2_SCLK,
+       GPIO14_SSP2_SFRM,
+       GPIO89_SSP2_TXD,
+       GPIO88_SSP2_RXD,
+
+       /* MMC */
+       GPIO32_MMC_CLK,
+       GPIO92_MMC_DAT_0,
+       GPIO109_MMC_DAT_1,
+       GPIO110_MMC_DAT_2,
+       GPIO111_MMC_DAT_3,
+       GPIO112_MMC_CMD,
+
+       /* LCD */
+       GPIO58_LCD_LDD_0,
+       GPIO59_LCD_LDD_1,
+       GPIO60_LCD_LDD_2,
+       GPIO61_LCD_LDD_3,
+       GPIO62_LCD_LDD_4,
+       GPIO63_LCD_LDD_5,
+       GPIO64_LCD_LDD_6,
+       GPIO65_LCD_LDD_7,
+       GPIO66_LCD_LDD_8,
+       GPIO67_LCD_LDD_9,
+       GPIO68_LCD_LDD_10,
+       GPIO69_LCD_LDD_11,
+       GPIO70_LCD_LDD_12,
+       GPIO71_LCD_LDD_13,
+       GPIO72_LCD_LDD_14,
+       GPIO73_LCD_LDD_15,
+       GPIO74_LCD_FCLK,
+       GPIO75_LCD_LCLK,
+       GPIO76_LCD_PCLK,
+       GPIO77_LCD_BIAS,
+
+       /* QCI */
+       GPIO12_CIF_DD_7,
+       GPIO17_CIF_DD_6,
+       GPIO50_CIF_DD_3,
+       GPIO51_CIF_DD_2,
+       GPIO52_CIF_DD_4,
+       GPIO53_CIF_MCLK,
+       GPIO54_CIF_PCLK,
+       GPIO55_CIF_DD_1,
+       GPIO81_CIF_DD_0,
+       GPIO82_CIF_DD_5,
+       GPIO84_CIF_FV,
+       GPIO85_CIF_LV,
+};
+
 /*
  * IRDA
  */
@@ -83,8 +165,64 @@ static struct platform_device gpio_keys = {
        .id   = -1,
 };
 
+
+/*
+ * EGPIO (Xilinx CPLD)
+ *
+ * 7 32-bit aligned 8-bit registers: 3x output, 1x irq, 3x input
+ */
+
+static struct resource egpio_resources[] = {
+       [0] = {
+               .start = PXA_CS3_PHYS,
+               .end   = PXA_CS3_PHYS + 0x20,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = gpio_to_irq(GPIO13_MAGICIAN_CPLD_IRQ),
+               .end   = gpio_to_irq(GPIO13_MAGICIAN_CPLD_IRQ),
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct htc_egpio_chip egpio_chips[] = {
+       [0] = {
+               .reg_start = 0,
+               .gpio_base = MAGICIAN_EGPIO(0, 0),
+               .num_gpios = 24,
+               .direction = HTC_EGPIO_OUTPUT,
+               .initial_values = 0x40, /* EGPIO_MAGICIAN_GSM_RESET */
+       },
+       [1] = {
+               .reg_start = 4,
+               .gpio_base = MAGICIAN_EGPIO(4, 0),
+               .num_gpios = 24,
+               .direction = HTC_EGPIO_INPUT,
+       },
+};
+
+static struct htc_egpio_platform_data egpio_info = {
+       .reg_width    = 8,
+       .bus_width    = 32,
+       .irq_base     = IRQ_BOARD_START,
+       .num_irqs     = 4,
+       .ack_register = 3,
+       .chip         = egpio_chips,
+       .num_chips    = ARRAY_SIZE(egpio_chips),
+};
+
+static struct platform_device egpio = {
+       .name          = "htc-egpio",
+       .id            = -1,
+       .resource      = egpio_resources,
+       .num_resources = ARRAY_SIZE(egpio_resources),
+       .dev = {
+               .platform_data = &egpio_info,
+       },
+};
+
 /*
- * LCD - Toppoly TD028STEB1
+ * LCD - Toppoly TD028STEB1 or Samsung LTP280QV
  */
 
 static struct pxafb_mode_info toppoly_modes[] = {
@@ -103,12 +241,99 @@ static struct pxafb_mode_info toppoly_modes[] = {
        },
 };
 
+static struct pxafb_mode_info samsung_modes[] = {
+       {
+               .pixclock     = 96153,
+               .bpp          = 16,
+               .xres         = 240,
+               .yres         = 320,
+               .hsync_len    = 8,
+               .vsync_len    = 4,
+               .left_margin  = 9,
+               .upper_margin = 4,
+               .right_margin = 9,
+               .lower_margin = 4,
+               .sync         = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+       },
+};
+
+static void toppoly_lcd_power(int on, struct fb_var_screeninfo *si)
+{
+       pr_debug("Toppoly LCD power\n");
+
+       if (on) {
+               pr_debug("on\n");
+               gpio_set_value(EGPIO_MAGICIAN_TOPPOLY_POWER, 1);
+               gpio_set_value(GPIO106_MAGICIAN_LCD_POWER_3, 1);
+               udelay(2000);
+               gpio_set_value(EGPIO_MAGICIAN_LCD_POWER, 1);
+               udelay(2000);
+               /* FIXME: enable LCDC here */
+               udelay(2000);
+               gpio_set_value(GPIO104_MAGICIAN_LCD_POWER_1, 1);
+               udelay(2000);
+               gpio_set_value(GPIO105_MAGICIAN_LCD_POWER_2, 1);
+       } else {
+               pr_debug("off\n");
+               msleep(15);
+               gpio_set_value(GPIO105_MAGICIAN_LCD_POWER_2, 0);
+               udelay(500);
+               gpio_set_value(GPIO104_MAGICIAN_LCD_POWER_1, 0);
+               udelay(1000);
+               gpio_set_value(GPIO106_MAGICIAN_LCD_POWER_3, 0);
+               gpio_set_value(EGPIO_MAGICIAN_LCD_POWER, 0);
+       }
+}
+
+static void samsung_lcd_power(int on, struct fb_var_screeninfo *si)
+{
+       pr_debug("Samsung LCD power\n");
+
+       if (on) {
+               pr_debug("on\n");
+               if (system_rev < 3)
+                       gpio_set_value(GPIO75_MAGICIAN_SAMSUNG_POWER, 1);
+               else
+                       gpio_set_value(EGPIO_MAGICIAN_LCD_POWER, 1);
+               mdelay(10);
+               gpio_set_value(GPIO106_MAGICIAN_LCD_POWER_3, 1);
+               mdelay(10);
+               gpio_set_value(GPIO104_MAGICIAN_LCD_POWER_1, 1);
+               mdelay(30);
+               gpio_set_value(GPIO105_MAGICIAN_LCD_POWER_2, 1);
+               mdelay(10);
+       } else {
+               pr_debug("off\n");
+               mdelay(10);
+               gpio_set_value(GPIO105_MAGICIAN_LCD_POWER_2, 0);
+               mdelay(30);
+               gpio_set_value(GPIO104_MAGICIAN_LCD_POWER_1, 0);
+               mdelay(10);
+               gpio_set_value(GPIO106_MAGICIAN_LCD_POWER_3, 0);
+               mdelay(10);
+               if (system_rev < 3)
+                       gpio_set_value(GPIO75_MAGICIAN_SAMSUNG_POWER, 0);
+               else
+                       gpio_set_value(EGPIO_MAGICIAN_LCD_POWER, 0);
+       }
+}
+
 static struct pxafb_mach_info toppoly_info = {
-       .modes       = toppoly_modes,
-       .num_modes   = 1,
-       .fixed_modes = 1,
-       .lccr0       = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-       .lccr3       = LCCR3_PixRsEdg,
+       .modes           = toppoly_modes,
+       .num_modes       = 1,
+       .fixed_modes     = 1,
+       .lccr0           = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+       .lccr3           = LCCR3_PixRsEdg,
+       .pxafb_lcd_power = toppoly_lcd_power,
+};
+
+static struct pxafb_mach_info samsung_info = {
+       .modes           = samsung_modes,
+       .num_modes       = 1,
+       .fixed_modes     = 1,
+       .lccr0           = LCCR0_LDDALT | LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+       .lccr3           = LCCR3_PixFlEdg,
+       .pxafb_lcd_power = samsung_lcd_power,
 };
 
 /*
@@ -120,9 +345,18 @@ static void magician_set_bl_intensity(int intensity)
        if (intensity) {
                PWM_CTRL0 = 1;
                PWM_PERVAL0 = 0xc8;
-               PWM_PWDUTY0 = intensity;
+               if (intensity > 0xc7) {
+                       PWM_PWDUTY0 = intensity - 0x48;
+                       gpio_set_value(EGPIO_MAGICIAN_BL_POWER2, 1);
+               } else {
+                       PWM_PWDUTY0 = intensity;
+                       gpio_set_value(EGPIO_MAGICIAN_BL_POWER2, 0);
+               }
+               gpio_set_value(EGPIO_MAGICIAN_BL_POWER, 1);
                pxa_set_cken(CKEN_PWM0, 1);
        } else {
+               /* PWM_PWDUTY0 = intensity; */
+               gpio_set_value(EGPIO_MAGICIAN_BL_POWER, 0);
                pxa_set_cken(CKEN_PWM0, 0);
        }
 }
@@ -130,18 +364,215 @@ static void magician_set_bl_intensity(int intensity)
 static struct generic_bl_info backlight_info = {
        .default_intensity = 0x64,
        .limit_mask        = 0x0b,
-       .max_intensity     = 0xc7,
+       .max_intensity     = 0xc7+0x48,
        .set_bl_intensity  = magician_set_bl_intensity,
 };
 
 static struct platform_device backlight = {
-       .name = "corgi-bl",
+       .name = "generic-bl",
        .dev  = {
                .platform_data = &backlight_info,
        },
        .id   = -1,
 };
 
+/*
+ * LEDs
+ */
+
+struct gpio_led gpio_leds[] = {
+       {
+               .name = "magician::vibra",
+               .default_trigger = "none",
+               .gpio = GPIO22_MAGICIAN_VIBRA_EN,
+       },
+       {
+               .name = "magician::phone_bl",
+               .default_trigger = "none",
+               .gpio = GPIO103_MAGICIAN_LED_KP,
+       },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+       .leds = gpio_leds,
+       .num_leds = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+       .name = "leds-gpio",
+       .id   = -1,
+       .dev  = {
+               .platform_data = &gpio_led_info,
+       },
+};
+
+static struct pasic3_led pasic3_leds[] = {
+       {
+               .led = {
+                       .name            = "magician:red",
+                       .default_trigger = "ds2760-battery.0-charging",
+               },
+               .hw_num = 0,
+               .bit2   = PASIC3_BIT2_LED0,
+               .mask   = PASIC3_MASK_LED0,
+       },
+       {
+               .led = {
+                       .name            = "magician:green",
+                       .default_trigger = "ds2760-battery.0-charging-or-full",
+               },
+               .hw_num = 1,
+               .bit2   = PASIC3_BIT2_LED1,
+               .mask   = PASIC3_MASK_LED1,
+       },
+       {
+               .led = {
+                       .name            = "magician:blue",
+                       .default_trigger = "bluetooth",
+               },
+               .hw_num = 2,
+               .bit2   = PASIC3_BIT2_LED2,
+               .mask   = PASIC3_MASK_LED2,
+       },
+};
+
+static struct platform_device pasic3;
+
+static struct pasic3_leds_machinfo __devinit pasic3_leds_info = {
+       .num_leds   = ARRAY_SIZE(pasic3_leds),
+       .power_gpio = EGPIO_MAGICIAN_LED_POWER,
+       .leds       = pasic3_leds,
+};
+
+/*
+ * PASIC3 with DS1WM
+ */
+
+static struct resource pasic3_resources[] = {
+       [0] = {
+               .start  = PXA_CS2_PHYS,
+               .end    = PXA_CS2_PHYS + 0x1b,
+               .flags  = IORESOURCE_MEM,
+       },
+       /* No IRQ handler in the PASIC3, DS1WM needs an external IRQ */
+       [1] = {
+               .start  = gpio_to_irq(GPIO107_MAGICIAN_DS1WM_IRQ),
+               .end    = gpio_to_irq(GPIO107_MAGICIAN_DS1WM_IRQ),
+               .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+       }
+};
+
+static struct pasic3_platform_data pasic3_platform_data = {
+       .bus_shift  = 2,
+       .led_pdata  = &pasic3_leds_info,
+       .clock_rate = 4000000,
+};
+
+static struct platform_device pasic3 = {
+       .name           = "pasic3",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(pasic3_resources),
+       .resource       = pasic3_resources,
+       .dev = {
+               .platform_data = &pasic3_platform_data,
+       },
+};
+
+/*
+ * External power
+ */
+
+static int magician_is_ac_online(void)
+{
+       return gpio_get_value(EGPIO_MAGICIAN_CABLE_STATE_AC);
+}
+
+static int magician_is_usb_online(void)
+{
+       return gpio_get_value(EGPIO_MAGICIAN_CABLE_STATE_USB);
+}
+
+static void magician_set_charge(int flags)
+{
+       gpio_set_value(GPIO30_MAGICIAN_nCHARGE_EN, !flags);
+       gpio_set_value(EGPIO_MAGICIAN_CHARGE_EN, flags);
+}
+
+static char *magician_supplicants[] = {
+       "ds2760-battery.0", "backup-battery"
+};
+
+static struct pda_power_pdata power_supply_info = {
+       .is_ac_online    = magician_is_ac_online,
+       .is_usb_online   = magician_is_usb_online,
+       .set_charge      = magician_set_charge,
+       .supplied_to     = magician_supplicants,
+       .num_supplicants = ARRAY_SIZE(magician_supplicants),
+};
+
+static struct resource power_supply_resources[] = {
+       [0] = {
+               .name  = "ac",
+               .flags = IORESOURCE_IRQ,
+               .start = IRQ_MAGICIAN_AC,
+               .end   = IRQ_MAGICIAN_AC,
+       },
+       [1] = {
+               .name  = "usb",
+               .flags = IORESOURCE_IRQ,
+               .start = IRQ_MAGICIAN_AC,
+               .end   = IRQ_MAGICIAN_AC,
+       },
+};
+
+static struct platform_device power_supply = {
+       .name = "pda-power",
+       .id   = -1,
+       .dev  = {
+               .platform_data = &power_supply_info,
+       },
+       .resource      = power_supply_resources,
+       .num_resources = ARRAY_SIZE(power_supply_resources),
+};
+
+
+/*
+ * MMC/SD
+ */
+
+static int magician_mci_init(struct device *dev,
+                               irq_handler_t detect_irq, void *data)
+{
+       return request_irq(IRQ_MAGICIAN_SD, detect_irq,
+                               IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+                               "MMC card detect", data);
+}
+
+static void magician_mci_setpower(struct device *dev, unsigned int vdd)
+{
+       struct pxamci_platform_data *pdata = dev->platform_data;
+
+       gpio_set_value(EGPIO_MAGICIAN_SD_POWER, (1 << vdd) & pdata->ocr_mask);
+}
+
+static int magician_mci_get_ro(struct device *dev)
+{
+       return (!gpio_get_value(EGPIO_MAGICIAN_nSD_READONLY));
+}
+
+static void magician_mci_exit(struct device *dev, void *data)
+{
+       free_irq(IRQ_MAGICIAN_SD, data);
+}
+
+static struct pxamci_platform_data magician_mci_info = {
+       .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+       .init     = magician_mci_init,
+       .get_ro   = magician_mci_get_ro,
+       .setpower = magician_mci_setpower,
+       .exit     = magician_mci_exit,
+};
+
 
 /*
  * USB OHCI
@@ -166,6 +597,11 @@ static struct pxaohci_platform_data magician_ohci_info = {
  * StrataFlash
  */
 
+static void magician_set_vpp(struct map_info *map, int vpp)
+{
+       gpio_set_value(EGPIO_MAGICIAN_FLASH_VPP, vpp);
+}
+
 #define PXA_CS_SIZE            0x04000000
 
 static struct resource strataflash_resource = {
@@ -176,13 +612,14 @@ static struct resource strataflash_resource = {
 
 static struct physmap_flash_data strataflash_data = {
        .width = 4,
+       .set_vpp = magician_set_vpp,
 };
 
 static struct platform_device strataflash = {
        .name          = "physmap-flash",
        .id            = -1,
-       .num_resources = 1,
        .resource      = &strataflash_resource,
+       .num_resources = 1,
        .dev = {
                .platform_data = &strataflash_data,
        },
@@ -194,16 +631,43 @@ static struct platform_device strataflash = {
 
 static struct platform_device *devices[] __initdata = {
        &gpio_keys,
+       &egpio,
        &backlight,
+       &pasic3,
+       &power_supply,
        &strataflash,
+       &leds_gpio,
 };
 
 static void __init magician_init(void)
 {
+       void __iomem *cpld;
+       int lcd_select;
+
+       pxa2xx_mfp_config(ARRAY_AND_SIZE(magician_pin_config));
+
        platform_add_devices(devices, ARRAY_SIZE(devices));
+       pxa_set_i2c_info(NULL);
+       pxa_set_mci_info(&magician_mci_info);
        pxa_set_ohci_info(&magician_ohci_info);
        pxa_set_ficp_info(&magician_ficp_info);
-       set_pxa_fb_info(&toppoly_info);
+
+       /* Check LCD type we have */
+       cpld = ioremap_nocache(PXA_CS3_PHYS, 0x1000);
+       if (cpld) {
+               u8 board_id = __raw_readb(cpld+0x14);
+               system_rev = board_id & 0x7;
+               lcd_select = board_id & 0x8;
+               iounmap(cpld);
+               pr_info("LCD type: %s\n", lcd_select ? "Samsung" : "Toppoly");
+               if (lcd_select && (system_rev < 3))
+                       pxa_gpio_mode(GPIO75_MAGICIAN_SAMSUNG_POWER_MD);
+               pxa_gpio_mode(GPIO104_MAGICIAN_LCD_POWER_1_MD);
+               pxa_gpio_mode(GPIO105_MAGICIAN_LCD_POWER_2_MD);
+               pxa_gpio_mode(GPIO106_MAGICIAN_LCD_POWER_3_MD);
+               set_pxa_fb_info(lcd_select ? &samsung_info : &toppoly_info);
+       } else
+               pr_err("LCD detection: CPLD mapping failed\n");
 }