diff options
Diffstat (limited to 'target/linux/xburst/patches-2.6.37/006-add-n516-board-support.patch')
-rw-r--r-- | target/linux/xburst/patches-2.6.37/006-add-n516-board-support.patch | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/target/linux/xburst/patches-2.6.37/006-add-n516-board-support.patch b/target/linux/xburst/patches-2.6.37/006-add-n516-board-support.patch new file mode 100644 index 0000000..c875f13 --- /dev/null +++ b/target/linux/xburst/patches-2.6.37/006-add-n516-board-support.patch @@ -0,0 +1,663 @@ +From fc2b1daf842a1b98472699fdddc8c12ce344bbd7 Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Sat, 24 Apr 2010 17:25:01 +0200 +Subject: [PATCH 06/23] Add n516 board support + +--- + arch/mips/include/asm/mach-jz4740/board-n516.h | 39 +++ + arch/mips/jz4740/Kconfig | 4 + + arch/mips/jz4740/Makefile | 1 + + arch/mips/jz4740/board-n516-display.c | 394 ++++++++++++++++++++++++ + arch/mips/jz4740/board-n516.c | 182 +++++++++++ + 5 files changed, 620 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/include/asm/mach-jz4740/board-n516.h + create mode 100644 arch/mips/jz4740/board-n516-display.c + create mode 100644 arch/mips/jz4740/board-n516.c + +--- /dev/null ++++ b/arch/mips/include/asm/mach-jz4740/board-n516.h +@@ -0,0 +1,39 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-n516.h ++ * ++ * JZ4730-based N516 board definition. ++ * ++ * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef __ASM_JZ4740_N516_H__ ++#define __ASM_JZ4740_N516_H__ ++ ++#include <asm/mach-jz4740/gpio.h> ++ ++/* ++ * GPIO ++ */ ++#define GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(17) ++#define GPIO_SD_CD_N JZ_GPIO_PORTD(7) ++#define GPIO_SD_WP JZ_GPIO_PORTD(15) ++#define GPIO_USB_DETECT JZ_GPIO_PORTD(19) ++#define GPIO_CHARG_STAT_N JZ_GPIO_PORTD(16) ++#define GPIO_LED_ENABLE JZ_GPIO_PORTD(28) ++#define GPIO_LPC_INT JZ_GPIO_PORTD(14) ++#define GPIO_HPHONE_DETECT JZ_GPIO_PORTD(20) ++#define GPIO_SPEAKER_ENABLE JZ_GPIO_PORTD(21) ++ ++/* Display */ ++#define GPIO_DISPLAY_RST_L JZ_GPIO_PORTB(18) ++#define GPIO_DISPLAY_RDY JZ_GPIO_PORTB(17) ++#define GPIO_DISPLAY_STBY JZ_GPIO_PORTC(22) ++#define GPIO_DISPLAY_ERR JZ_GPIO_PORTC(23) ++#define GPIO_DISPLAY_OFF_N JZ_GPIO_PORTD(1) ++ ++#endif /* __ASM_JZ4740_N516_H__ */ +--- a/arch/mips/jz4740/Kconfig ++++ b/arch/mips/jz4740/Kconfig +@@ -6,6 +6,10 @@ choice + config JZ4740_QI_LB60 + bool "Qi Hardware Ben NanoNote" + ++config JZ4740_N516 ++ bool "Hanvon n516 eBook reader" ++ select SOC_JZ4740 ++ + endchoice + + config HAVE_PWM +--- a/arch/mips/jz4740/Makefile ++++ b/arch/mips/jz4740/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs. + # board specific support + + obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o ++obj-$(CONFIG_JZ4740_N516) += board-n516.o board-n516-display.o + + # PM support + +--- /dev/null ++++ b/arch/mips/jz4740/board-n516-display.c +@@ -0,0 +1,394 @@ ++/* ++ * board-n516-display.c -- Platform device for N516 display ++ * ++ * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com> ++ * ++ * 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 <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/fb.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++#include <linux/gpio.h> ++ ++#include <asm/mach-jz4740/jz4740_fb.h> ++ ++#include <asm/mach-jz4740/platform.h> ++#include <asm/mach-jz4740/board-n516.h> ++ ++#include <video/metronomefb.h> ++#include <linux/console.h> ++ ++static struct fb_videomode n516_fb_modes[] = { ++ [0] = { ++ .name = "Metronome 800x600", ++ .refresh = 50, ++ .xres = 400, ++ .yres = 624, ++ .hsync_len = 31, ++ .vsync_len = 23, ++ .right_margin = 31, ++ .left_margin = 5, ++ .upper_margin = 1, ++ .lower_margin = 2, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ }, ++}; ++ ++static struct jz4740_fb_platform_data n516_fb_pdata = { ++ .num_modes = ARRAY_SIZE(n516_fb_modes), ++ .modes = n516_fb_modes, ++ .bpp = 16, ++ .lcd_type = JZ_LCD_TYPE_GENERIC_16_BIT, ++}; ++ ++struct n516_board_info { ++ uint8_t *metromem; ++ size_t wfm_size; ++ struct fb_info *host_fbinfo; /* the host LCD controller's fbi */ ++ unsigned int fw; ++ unsigned int fh; ++}; ++ ++static struct platform_device *n516_device; ++static struct n516_board_info n516_board_info; ++ ++static int metronome_gpios[] = { ++ GPIO_DISPLAY_STBY, ++ GPIO_DISPLAY_RST_L, ++ GPIO_DISPLAY_RDY, ++ GPIO_DISPLAY_ERR, ++/* GPIO_DISPLAY_OFF_N,*/ ++}; ++ ++static const char *metronome_gpio_names[] = { ++ "Metronome STDBY", ++ "Metronome RST", ++ "Metronome RDY", ++ "Metronome ERR", ++/* "Metronone OFF",*/ ++}; ++ ++static int n516_enable_hostfb(bool enable) ++{ ++ int ret; ++ int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; ++ ++ acquire_console_sem(); ++ ret = fb_blank(n516_board_info.host_fbinfo, blank); ++ release_console_sem(); ++ ++ return ret; ++} ++ ++static int n516_init_metronome_gpios(struct metronomefb_par *par) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) { ++ ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]); ++ if (ret) ++ goto err; ++ } ++ ++ gpio_direction_output(GPIO_DISPLAY_OFF_N, 0); ++ gpio_direction_output(GPIO_DISPLAY_RST_L, 0); ++ gpio_direction_output(GPIO_DISPLAY_STBY, 0); ++ gpio_direction_input(GPIO_DISPLAY_RDY); ++ gpio_direction_input(GPIO_DISPLAY_ERR); ++ ++ return 0; ++err: ++ for (--i; i >= 0; --i) ++ gpio_free(metronome_gpios[i]); ++ ++ return ret; ++} ++ ++static int n516_share_video_mem(struct fb_info *info) ++{ ++ int ret; ++ ++ dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); ++ dev_dbg(&n516_device->dev, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__, info->var.xres, info->var.yres); ++ /* rough check if this is our desired fb and not something else */ ++ if ((info->var.xres != n516_fb_pdata.modes[0].xres) ++ || (info->var.yres != n516_fb_pdata.modes[0].yres)) ++ return 0; ++ ++ /* we've now been notified that we have our new fb */ ++ n516_board_info.metromem = info->screen_base; ++ n516_board_info.host_fbinfo = info; ++ ++ n516_enable_hostfb(false); ++ /* try to refcount host drv since we are the consumer after this */ ++ if (!try_module_get(info->fbops->owner)) ++ return -ENODEV; ++ ++ /* this _add binds metronomefb to n516. metronomefb refcounts n516 */ ++ ret = platform_device_add(n516_device); ++ ++ if (ret) { ++ platform_device_put(n516_device); ++ return ret; ++ } ++ ++ /* request our platform independent driver */ ++ request_module("metronomefb"); ++ ++ return 0; ++} ++ ++static int n516_unshare_video_mem(struct fb_info *info) ++{ ++ dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); ++ ++ if (info != n516_board_info.host_fbinfo) ++ return 0; ++ ++ module_put(n516_board_info.host_fbinfo->fbops->owner); ++ return 0; ++} ++ ++static int n516_fb_notifier_callback(struct notifier_block *self, ++ unsigned long event, void *data) ++{ ++ struct fb_event *evdata = data; ++ struct fb_info *info = evdata->info; ++ ++ dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); ++ ++ if (event == FB_EVENT_FB_REGISTERED) ++ return n516_share_video_mem(info); ++ else if (event == FB_EVENT_FB_UNREGISTERED) ++ return n516_unshare_video_mem(info); ++ ++ return 0; ++} ++ ++static struct notifier_block n516_fb_notif = { ++ .notifier_call = n516_fb_notifier_callback, ++}; ++ ++/* this gets called as part of our init. these steps must be done now so ++ * that we can use set_pxa_fb_info */ ++static void __init n516_presetup_fb(void) ++{ ++ int padding_size; ++ int totalsize; ++ ++ /* the frame buffer is divided as follows: ++ command | CRC | padding ++ 16kb waveform data | CRC | padding ++ image data | CRC ++ */ ++ ++ n516_board_info.fw = 800; ++ n516_board_info.fh = 624; ++ ++ /* waveform must be 16k + 2 for checksum */ ++ n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw); ++ ++ padding_size = PAGE_SIZE + (4 * n516_board_info.fw); ++ ++ /* total is 1 cmd , 1 wfm, padding and image */ ++ totalsize = n516_board_info.fw + n516_board_info.wfm_size; ++ totalsize += padding_size + (n516_board_info.fw*n516_board_info.fh); ++ ++ /* save this off because we're manipulating fw after this and ++ * we'll need it when we're ready to setup the framebuffer */ ++ ++ /* the reason we do this adjustment is because we want to acquire ++ * more framebuffer memory without imposing custom awareness on the ++ * underlying driver */ ++ n516_fb_pdata.modes[0].yres = DIV_ROUND_UP(totalsize, n516_board_info.fw); ++ ++ jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata; ++ platform_device_register(&jz4740_framebuffer_device); ++} ++ ++/* this gets called by metronomefb as part of its init, in our case, we ++ * have already completed initial framebuffer init in presetup_fb so we ++ * can just setup the fb access pointers */ ++static int n516_setup_fb(struct metronomefb_par *par) ++{ ++ /* metromem was set up by the notifier in share_video_mem so now ++ * we can use its value to calculate the other entries */ ++ par->metromem_cmd = (struct metromem_cmd *) n516_board_info.metromem; ++ par->metromem_wfm = n516_board_info.metromem + n516_board_info.fw; ++ par->metromem_img = par->metromem_wfm + n516_board_info.wfm_size; ++ par->metromem_img_csum = (u16 *) (par->metromem_img + (n516_board_info.fw * n516_board_info.fh)); ++ par->metromem_dma = n516_board_info.host_fbinfo->fix.smem_start; ++ ++ return 0; ++} ++ ++static int n516_get_panel_type(void) ++{ ++ return 5; ++} ++ ++static irqreturn_t n516_handle_irq(int irq, void *dev_id) ++{ ++ struct metronomefb_par *par = dev_id; ++ ++ dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY)); ++ wake_up_all(&par->waitq); ++ ++ return IRQ_HANDLED; ++} ++ ++static void n516_power_ctl(struct metronomefb_par *par, int cmd) ++{ ++ switch (cmd) { ++ case METRONOME_POWER_OFF: ++ gpio_set_value(GPIO_DISPLAY_OFF_N, 1); ++ n516_enable_hostfb(false); ++ break; ++ case METRONOME_POWER_ON: ++ gpio_set_value(GPIO_DISPLAY_OFF_N, 0); ++ n516_enable_hostfb(true); ++ break; ++ } ++} ++ ++static int n516_get_rdy(struct metronomefb_par *par) ++{ ++ return gpio_get_value(GPIO_DISPLAY_RDY); ++} ++ ++static int n516_get_err(struct metronomefb_par *par) ++{ ++ return gpio_get_value(GPIO_DISPLAY_ERR); ++} ++ ++static int n516_setup_irq(struct fb_info *info) ++{ ++ int ret; ++ ++ dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); ++ ++ ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq, ++ IRQF_TRIGGER_RISING, ++ "n516", info->par); ++ if (ret) ++ dev_err(&n516_device->dev, "request_irq failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static void n516_set_rst(struct metronomefb_par *par, int state) ++{ ++ dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ if (state) ++ gpio_set_value(GPIO_DISPLAY_RST_L, 1); ++ else ++ gpio_set_value(GPIO_DISPLAY_RST_L, 0); ++} ++ ++static void n516_set_stdby(struct metronomefb_par *par, int state) ++{ ++ dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ if (state) ++ gpio_set_value(GPIO_DISPLAY_STBY, 1); ++ else ++ gpio_set_value(GPIO_DISPLAY_STBY, 0); ++} ++ ++static int n516_wait_event(struct metronomefb_par *par) ++{ ++ unsigned long timeout = jiffies + HZ / 20; ++ ++ dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n", ++ __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ while (n516_get_rdy(par) && time_before(jiffies, timeout)) ++ schedule(); ++ ++ dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n", ++ __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ return wait_event_timeout(par->waitq, ++ n516_get_rdy(par), HZ * 2) ? 0 : -EIO; ++} ++ ++static int n516_wait_event_intr(struct metronomefb_par *par) ++{ ++ unsigned long timeout = jiffies + HZ/20; ++ ++ dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n", ++ __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ while (n516_get_rdy(par) && time_before(jiffies, timeout)) ++ schedule(); ++ ++ dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n", ++ __func__, gpio_get_value(GPIO_DISPLAY_RDY)); ++ return wait_event_interruptible_timeout(par->waitq, ++ n516_get_rdy(par), HZ * 2) ? 0 : -EIO; ++} ++ ++static void n516_cleanup(struct metronomefb_par *par) ++{ ++ int i; ++ ++ free_irq(gpio_to_irq(GPIO_DISPLAY_RDY), par); ++ for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) ++ gpio_free(metronome_gpios[i]); ++} ++ ++static struct metronome_board n516_board __initdata = { ++ .owner = THIS_MODULE, ++ .power_ctl = n516_power_ctl, ++ .setup_irq = n516_setup_irq, ++ .setup_io = n516_init_metronome_gpios, ++ .setup_fb = n516_setup_fb, ++ .set_rst = n516_set_rst, ++ .get_err = n516_get_err, ++ .get_rdy = n516_get_rdy, ++ .set_stdby = n516_set_stdby, ++ .met_wait_event = n516_wait_event, ++ .met_wait_event_intr = n516_wait_event_intr, ++ .get_panel_type = n516_get_panel_type, ++ .cleanup = n516_cleanup, ++}; ++ ++static int __init n516_init(void) ++{ ++ int ret; ++ ++ /* Keep the metronome off, until its driver is loaded */ ++ ret = gpio_request(GPIO_DISPLAY_OFF_N, "Display off"); ++ if (ret) ++ return ret; ++ ++ gpio_direction_output(GPIO_DISPLAY_OFF_N, 1); ++ ++ /* before anything else, we request notification for any fb ++ * creation events */ ++ fb_register_client(&n516_fb_notif); ++ ++ n516_device = platform_device_alloc("metronomefb", -1); ++ if (!n516_device) ++ return -ENOMEM; ++ ++ /* the n516_board that will be seen by metronomefb is a copy */ ++ platform_device_add_data(n516_device, &n516_board, ++ sizeof(n516_board)); ++ ++ n516_presetup_fb(); ++ ++ return 0; ++} ++module_init(n516_init); ++ ++MODULE_DESCRIPTION("board driver for n516 display"); ++MODULE_AUTHOR("Yauhen Kharuzhy"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/mips/jz4740/board-n516.c +@@ -0,0 +1,182 @@ ++/* ++ * linux/arch/mips/jz4740/board-516.c ++ * ++ * JZ4740 n516 board setup routines. ++ * ++ * Copyright (c) 2009, Yauhen Kharuzhy <jekhor@gmail.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/ioport.h> ++#include <linux/mm.h> ++#include <linux/console.h> ++#include <linux/delay.h> ++#include <linux/i2c.h> ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/leds.h> ++ ++#include <linux/power_supply.h> ++#include <linux/power/gpio-charger.h> ++ ++#include <linux/i2c.h> ++ ++#include <asm/mach-jz4740/jz4740_mmc.h> ++#include <asm/mach-jz4740/jz4740_nand.h> ++ ++#include <asm/mach-jz4740/board-n516.h> ++#include <asm/mach-jz4740/platform.h> ++ ++#include "clock.h" ++ ++static long n516_panic_blink(int state) ++{ ++ gpio_set_value(GPIO_LED_ENABLE, state ? 1 : 0); ++ return 0; ++} ++ ++static void __init board_gpio_setup(void) ++{ ++/* jz_gpio_enable_pullup(JZ_GPIO_PORTD(23)); ++ jz_gpio_enable_pullup(JZ_GPIO_PORTD(24));*/ ++} ++ ++static const struct i2c_board_info n516_i2c_board_info[] = { ++ { ++ .type = "LPC524", ++ .addr = 0x54, ++ }, ++ { ++ .type = "lm75a", ++ .addr = 0x48, ++ } ++}; ++ ++static struct jz4740_mmc_platform_data n516_mmc_pdata = { ++ .gpio_card_detect = GPIO_SD_CD_N, ++ .card_detect_active_low = 1, ++ .gpio_read_only = -1, ++ .gpio_power = GPIO_SD_VCC_EN_N, ++ .power_active_low = 1, ++}; ++ ++static struct gpio_led n516_leds[] = { ++ { ++ .name = "n516:blue:power", ++ .gpio = GPIO_LED_ENABLE, ++ .default_state = LEDS_GPIO_DEFSTATE_ON, ++ .default_trigger = "nand-disk", ++ } ++}; ++ ++static struct gpio_led_platform_data n516_leds_pdata = { ++ .leds = n516_leds, ++ .num_leds = ARRAY_SIZE(n516_leds), ++}; ++ ++static struct platform_device n516_leds_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &n516_leds_pdata, ++ }, ++}; ++ ++static struct mtd_partition n516_partitions[] = { ++ { .name = "NAND BOOT partition", ++ .offset = 0 * 0x100000, ++ .size = 4 * 0x100000, ++ }, ++ { .name = "NAND KERNEL partition", ++ .offset = 4 * 0x100000, ++ .size = 4 * 0x100000, ++ }, ++ { .name = "NAND ROOTFS partition", ++ .offset = 8 * 0x100000, ++ .size = 504 * 0x100000, ++ }, ++}; ++ ++static struct nand_ecclayout n516_ecclayout = { ++ .eccbytes = 36, ++ .eccpos = { ++ 6, 7, 8, 9, 10, 11, 12, 13, 14, ++ 15, 16, 17, 18, 19, 20, 21, 22, 23, ++ 24, 25, 26, 27, 28, 29, 30, 31, 32, ++ 33, 34, 35, 36, 37, 38, 39, 40, 41, ++ }, ++ .oobfree = { ++ { ++ .offset = 2, ++ .length = 4 ++ }, ++ { ++ .offset = 42, ++ .length = 22, ++ } ++ } ++}; ++ ++static struct jz_nand_platform_data n516_nand_pdata = { ++ .ecc_layout = &n516_ecclayout, ++ .partitions = n516_partitions, ++ .num_partitions = ARRAY_SIZE(n516_partitions), ++ .busy_gpio = 94, ++}; ++ ++static char *n516_batteries[] = { ++ "n516_battery", ++}; ++ ++static struct gpio_charger_platform_data n516_charger_pdata = { ++ .name = "usb", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .gpio = GPIO_USB_DETECT, ++ .gpio_active_low = 1, ++ .batteries = n516_batteries, ++ .num_batteries = ARRAY_SIZE(n516_batteries), ++}; ++ ++static struct platform_device n516_charger_device = { ++ .name = "gpio-charger", ++ .dev = { ++ .platform_data = &n516_charger_pdata, ++ }, ++}; ++ ++static struct platform_device *n516_devices[] __initdata = { ++ &jz4740_nand_device, ++ &n516_leds_device, ++ &jz4740_mmc_device, ++ &jz4740_i2s_device, ++ &jz4740_codec_device, ++ &jz4740_rtc_device, ++ &jz4740_udc_device, ++ &jz4740_i2c_device, ++ &n516_charger_device, ++}; ++ ++struct jz4740_clock_board_data jz4740_clock_bdata = { ++ .ext_rate = 12000000, ++ .rtc_rate = 32768, ++}; ++ ++static int n516_setup_platform(void) ++{ ++ jz4740_serial_device_register(); ++ board_gpio_setup(); ++ ++ panic_blink = n516_panic_blink; ++ i2c_register_board_info(0, n516_i2c_board_info, ARRAY_SIZE(n516_i2c_board_info)); ++ jz4740_mmc_device.dev.platform_data = &n516_mmc_pdata; ++ jz4740_nand_device.dev.platform_data = &n516_nand_pdata; ++ ++ return platform_add_devices(n516_devices, ARRAY_SIZE(n516_devices)); ++} ++arch_initcall(n516_setup_platform); |