Signed-off-by: Frederic Pecourt <opengemini at free.fr>

Index: target/linux/gemini/image/Makefile
===================================================================
--- target/linux/gemini/image/Makefile  (révision 23121)
+++ target/linux/gemini/image/Makefile  (copie de travail)
@@ -14,6 +14,9 @@
 # WBD222: mach id 2753 (0xAC1)
        echo -en "\x0a\x1c\xa0\xe3\xc1\x10\x81\xe3" > 
$(KDIR)/$(IMG_PREFIX)-wbd222-zImage
        cat $(LINUX_DIR)/arch/arm/boot/zImage >> 
$(KDIR)/$(IMG_PREFIX)-wbd222-zImage
+# mzknas: for now same mach id as nas4220b 2038 (0x7F6)
+       echo -en "\x07\x1c\xa0\xe3\xf6\x10\x81\xe3" > 
$(KDIR)/$(IMG_PREFIX)-mzknas-zImage
+       cat $(LINUX_DIR)/arch/arm/boot/zImage >> 
$(KDIR)/$(IMG_PREFIX)-mzknas-zImage
 endef
 
 define Image/BuildKernel
@@ -22,6 +25,8 @@
        cat $(KDIR)/$(IMG_PREFIX)-wbd111-zImage >> 
$(BIN_DIR)/$(IMG_PREFIX)-wbd111-zImage
        echo -en 
"\x00\x00\xa0\xe1\x00\x00\xa0\xe1\x00\x00\xa0\xe1\x00\x00\xa0\xe1" > 
$(BIN_DIR)/$(IMG_PREFIX)-wbd222-zImage
        cat $(KDIR)/$(IMG_PREFIX)-wbd222-zImage >> 
$(BIN_DIR)/$(IMG_PREFIX)-wbd222-zImage
+       echo -en 
"\x00\x00\xa0\xe1\x00\x00\xa0\xe1\x00\x00\xa0\xe1\x00\x00\xa0\xe1" > 
$(BIN_DIR)/$(IMG_PREFIX)-mzknas-zImage
+       cat $(KDIR)/$(IMG_PREFIX)-mzknas-zImage >> 
$(BIN_DIR)/$(IMG_PREFIX)-mzknas-zImage
 endef
 
 define Image/Build/jffs2-64k
Index: target/linux/gemini/files/arch/arm/mach-gemini/board-mzknas.c
===================================================================
--- target/linux/gemini/files/arch/arm/mach-gemini/board-mzknas.c       
(révision 0)
+++ target/linux/gemini/files/arch/arm/mach-gemini/board-mzknas.c       
(révision 0)
@@ -0,0 +1,209 @@
+/*
+ *  Support for PLANEX MZK-NAS02SG
+ *
+ *  Copyright (C) 2010 Frederic PECOURT
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/skbuff.h>
+#include <linux/gpio_keys.h>
+#include <linux/mdio-gpio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+#include <mach/gmac.h>
+
+#include "common.h"
+#include <mach/hardware.h>
+#include <mach/global_reg.h>
+#define PFLASH_SHARE_BIT       0x02
+
+static struct mdio_gpio_platform_data planex_mdio = {
+       .mdc            = 22,
+       .mdio           = 21,
+       .phy_mask       = ~((1 << 1) | (1 << 2)),
+};
+
+static struct platform_device planex_phy_device = {
+       .name   = "mdio-gpio",
+       .id     = 0,
+       .dev    = {
+               .platform_data = &planex_mdio,
+       },
+};
+
+static struct gemini_gmac_platform_data gmac_data = {
+       .bus_id[0] = "0:01",
+       .interface[0] = PHY_INTERFACE_MODE_RGMII,
+       .bus_id[1] = "0:02",
+       .interface[1] = PHY_INTERFACE_MODE_RGMII,
+};
+
+static struct gpio_keys_button planex_keys[] = {
+       {
+               .code           = KEY_SETUP,
+               .gpio           = 61,
+               .active_low     = 1,
+               .desc           = "Backup Button",
+               .type           = EV_KEY,
+       },
+       {
+               .code           = KEY_RESTART,
+               .gpio           = 63,
+               .active_low     = 1,
+               .desc           = "Softreset Button",
+               .type           = EV_KEY,
+       },
+};
+
+static struct gpio_keys_platform_data planex_keys_data = {
+       .buttons        = planex_keys,
+       .nbuttons       = ARRAY_SIZE(planex_keys),
+};
+
+static struct platform_device planex_keys_device = {
+       .name   = "gpio-keys",
+       .id     = -1,
+       .dev    = {
+               .platform_data = &planex_keys_data,
+       },
+};
+
+static struct gpio_led planex_leds[] = {
+       {
+               .name                   = "planex:blue:sata1",
+               .default_trigger        = "ata-disk1",
+               .default_state  = 1,
+               .gpio                   = 18,
+               .active_low = 1,
+       },
+       {
+               .name                   = "planex:blue:sata0",
+               .default_trigger        = "ata-disk0",
+               .default_state  = 1,
+               .gpio                   = 17,
+               .active_low = 1,
+       },
+       {
+               .name                   = "planex:blue:usb2",
+               .default_trigger        = "usb2",
+               .default_state  = 1,
+               .gpio                   = 4, 
+               .active_low = 1,
+       },
+       {
+               .name                   = "planex:blue:usb1",
+               .default_trigger        = "usb1",
+               .default_state  = 1,
+               .gpio                   = 6, 
+               .active_low = 1,
+       },
+       {
+               .name                   = "planex:blue:power",
+               .default_trigger        = "default-on",
+               .default_state  = 1,
+               .gpio                   = 7,
+               .active_low = 1,
+       },
+};
+
+static struct gpio_led_platform_data planex_leds_data = {
+       .num_leds       = ARRAY_SIZE(planex_leds),
+       .leds           = planex_leds,
+};
+  
+static struct platform_device planex_leds_device = {
+       .name   = "leds-gpio",
+       .id     = -1,
+       .dev    = {
+               .platform_data = &planex_leds_data,
+       },
+};
+
+static struct sys_timer planex_timer = {
+       .init   = gemini_timer_init,
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition planex_partitions[] = {
+       {
+               .name           = "RedBoot",
+               .offset         = 0,
+               .size           = 0x020000,
+               .mask_flags     = MTD_WRITEABLE,
+       } , {
+               .name           = "Kernel",
+               .offset         = 0x020000,
+               .size           = 0x700000,
+       } , {
+               .name           = "VCTL",
+               .offset         = 0x720000,
+               .size           = 0x020000,
+               .mask_flags     = MTD_WRITEABLE,
+       } , {
+               .name           = "CurConf",
+               .offset         = 0x740000,
+               .size           = 0x0A0000,
+               /*.mask_flags   = MTD_WRITEABLE,*/
+       } , {
+               .name           = "FIS",
+               .offset         = 0x7E0000,
+               .size           = 0x020000,
+               .mask_flags     = MTD_WRITEABLE,
+       }
+};
+#define planex_num_partitions  ARRAY_SIZE(planex_partitions)
+#else
+#define planex_partitions      NULL
+#define planex_num_partitions  0
+#endif /* CONFIG_MTD_PARTITIONS */
+
+static void __init planex_init(void)
+{
+       unsigned int value ;
+
+       gemini_gpio_init();
+       platform_register_uart();
+       platform_register_watchdog();
+#ifdef CONFIG_MTD_PARTITIONS
+       platform_register_pflash(SZ_8M, 
planex_partitions,planex_num_partitions);
+#else
+       platform_register_pflash(SZ_8M, NULL,0);
+#endif
+       platform_register_pwc();
+       platform_register_rtc();
+       platform_device_register(&planex_leds_device);
+       platform_device_register(&planex_keys_device);
+       platform_device_register(&planex_phy_device);
+       platform_register_ethernet(&gmac_data);
+       platform_register_usb(0);
+       platform_register_usb(1);
+       platform_register_pata(1);
+       value = readl(IO_ADDRESS((GEMINI_GLOBAL_BASE) + GLOBAL_MISC_CTRL));
+       value = value & (~PFLASH_SHARE_BIT) ;
+       writel(value,IO_ADDRESS((GEMINI_GLOBAL_BASE) +GLOBAL_MISC_CTRL));
+
+}
+
+// for now, no need for a new machine ID 
+// which is anyway faked in the boot makefile
+MACHINE_START(NAS4220B, "PLANEX MZK-NAS02SG")
+       .phys_io        = 0x7fffc000,
+       .io_pg_offst    = ((0xffffc000) >> 18) & 0xfffc,
+       .boot_params    = 0x100,
+       .map_io         = gemini_map_io,
+       .init_irq       = gemini_init_irq,
+       .timer          = &planex_timer,
+       .init_machine   = planex_init,
+MACHINE_END
Index: target/linux/gemini/files/drivers/ata/pata_gemini.c
===================================================================
--- target/linux/gemini/files/drivers/ata/pata_gemini.c (révision 0)
+++ target/linux/gemini/files/drivers/ata/pata_gemini.c (révision 0)
@@ -0,0 +1,268 @@
+/*
+ *  Support for Gemini PATA
+ *
+ *  Copyright (C) 2009 Janos Laube <[email protected]>
+ *  Copyright (C) 2010 Frederic Pecourt <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Values of IOMUX
+ * 26:24 bits is "IDE IO Select"
+ * 111:100 - Reserved
+ * 011 - ata0 <-> sata0, sata1; bring out ata1
+ * 010 - ata1 <-> sata1, sata0; bring out ata0
+ * 001 - ata0 <-> sata0, ata1 <-> sata1; bring out ata1
+ * 000 - ata0 <-> sata0, ata1 <-> sata1; bring out ata0
+ *
+ */
+
+
+
+
+#include <linux/platform_device.h>
+#include <linux/libata.h>
+#include <linux/leds.h>
+
+#include <mach/hardware.h>
+#include <mach/global_reg.h>
+
+#define DRV_NAME               "pata_gemini"
+#define DRV_VERSION            "0.11"
+
+#define GEMINI_PATA_PORTS      1
+#define PIO_TIMING_REG         (ap->ioaddr.bmdma_addr + 0x10)
+#define MDMA_TIMING_REG                (ap->ioaddr.bmdma_addr + 0x11)
+#define UDMA_TIMING0_REG       (ap->ioaddr.bmdma_addr + 0x12)
+#define UDMA_TIMING1_REG       (ap->ioaddr.bmdma_addr + 0x13)
+#define CLK_MOD_REG            (ap->ioaddr.bmdma_addr + 0x14)
+
+static unsigned char PIO_TIMING[5] = {
+       0xaa, 0xa3, 0xa1, 0x33, 0x31
+};
+
+static unsigned char TIMING_UDMA_50M[6] = {
+       0x33, 0x31, 0x21, 0x21, 0x11, 0x91
+};
+
+static unsigned char TIMING_UDMA_66M[7] = {
+       0x44, 0x42, 0x31, 0x21, 0x11, 0x91, 0x91
+};
+
+static struct scsi_host_template gemini_pata_sht = {
+       ATA_NCQ_SHT(DRV_NAME),
+       .can_queue      = 1,
+       .sg_tablesize   = 128,
+       .dma_boundary   = 0xffffU,
+};
+
+static void gemini_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+       unsigned int udma       = adev->dma_mode;
+       u8 speed                = udma;
+       unsigned int drive_dn   = adev->devno;
+       u8 reg;
+
+       reg = ioread8(CLK_MOD_REG);
+       reg |= (1 << (4 + (drive_dn & 0x01)));
+       iowrite8(reg, CLK_MOD_REG);
+
+       if ((speed == XFER_UDMA_6) || (speed == XFER_UDMA_3)
+               || (speed == XFER_UDMA_4) || (speed & XFER_MW_DMA_0))
+       {
+               reg = ioread8(CLK_MOD_REG);
+               reg |= (1 << (drive_dn & 0x01));
+               iowrite8(reg, CLK_MOD_REG);
+               iowrite8(TIMING_UDMA_66M[speed & ~XFER_UDMA_0],
+                       UDMA_TIMING0_REG);
+       }
+       else
+       {
+               reg = ioread8(CLK_MOD_REG);
+               reg &= ~(1 << (drive_dn & 0x01));
+               iowrite8(reg, CLK_MOD_REG);
+               iowrite8(TIMING_UDMA_50M[speed & ~XFER_UDMA_0],
+                       UDMA_TIMING0_REG);
+       }
+       return;
+}
+
+static void gemini_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+       unsigned int pio = adev->pio_mode - XFER_PIO_0;
+       iowrite8(PIO_TIMING[pio], PIO_TIMING_REG);
+}
+
+unsigned int gemini_qc_issue(struct ata_queued_cmd *qc)
+{
+       ledtrig_ide_activity();
+       return ata_sff_qc_issue(qc);
+}
+
+static struct ata_port_operations gemini_pata_port_ops = {
+       .inherits       = &ata_bmdma_port_ops,
+       .set_dmamode    = gemini_set_dmamode,
+       .set_piomode    = gemini_set_piomode,
+       .qc_issue       = gemini_qc_issue,
+};
+
+static struct ata_port_info gemini_pata_portinfo = {
+       .flags          = ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | 
ATA_FLAG_SLAVE_POSS,
+       .udma_mask= ATA_UDMA7,
+       .pio_mask       = ATA_PIO4,
+       .port_ops       = &gemini_pata_port_ops,
+};
+
+static irqreturn_t gemini_pata_interrupt(int irq, void *dev)
+{
+       struct ata_host *host = dev;
+       unsigned int i, handled = 0;
+
+       spin_lock_irq(&host->lock);
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap;
+
+               ap = host->ports[i];
+               if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {
+                       struct ata_queued_cmd *qc;
+                       qc = ata_qc_from_tag(ap, ap->link.active_tag);
+
+                       if (qc && (qc->tf.ctl & ATA_NIEN))
+                               ap->ops->sff_check_status(ap);
+                       else if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
+                                       (qc->flags & ATA_QCFLAG_ACTIVE))
+                               handled |= ata_sff_host_intr(ap, qc);
+                       else
+                               ap->ops->sff_check_status(ap);
+               }
+               else
+                       ap->ops->sff_check_status(ap);
+       }
+       spin_unlock_irq(&host->lock);
+
+       return IRQ_RETVAL(handled);
+}
+
+static int gemini_pata_platform_probe(struct platform_device *pdev)
+{
+       struct ata_host *host;
+       struct resource *res;
+       const struct ata_port_info *ppi[] = {&gemini_pata_portinfo, 0};
+       unsigned int irq, i;
+       void __iomem *mmio_base;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res)
+               return -ENODEV;
+       irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       mmio_base = devm_ioremap(&pdev->dev, res->start,
+               res->end - res->start + 1);
+
+       printk("pata_gemini: configuring port with irq %d, base %p\n",
+                       irq, mmio_base);
+
+       host = ata_host_alloc_pinfo(&pdev->dev, ppi, GEMINI_PATA_PORTS);
+       if (!host)
+               return -ENOMEM;
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+               struct ata_ioports *ioaddr = &ap->ioaddr;
+
+               ioaddr->bmdma_addr              = mmio_base;
+               ioaddr->cmd_addr                = mmio_base + 0x20;
+               ioaddr->altstatus_addr  =
+               ioaddr->ctl_addr                = mmio_base + 0x36;
+               ata_sff_std_ports(ioaddr);
+               host->ports[i]->cbl = ATA_CBL_SATA;
+       }
+
+       return ata_host_activate(host, irq, gemini_pata_interrupt,
+               IRQF_SHARED, &gemini_pata_sht);
+}
+
+static int gemini_pata_platform_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct ata_host *host = dev_get_drvdata(dev);
+       ata_host_detach(host);
+       return 0;
+}
+
+static struct platform_driver gemini_pata_driver = {
+       .probe          = gemini_pata_platform_probe,
+       .remove         = gemini_pata_platform_remove,
+       .driver = {
+               .name = DRV_NAME,
+       }
+};
+
+static int __init gemini_pata_module_init(void)
+{
+       unsigned int reg;
+       u8 phy_status;
+       unsigned long timeout = jiffies + (HZ * 1);
+
+       /* iomux 2, slave mode */
+       reg = readl(IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_MISC_CTRL);
+       reg &= ~0x07000000;
+//     reg |= 0x02000012;
+       reg |= 0x02000010;
+  writel(reg, IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_MISC_CTRL);
+
+       /* enabling ports for presence detection */
+       writel(0x00000003, IO_ADDRESS(GEMINI_SATA_BASE) + 0x18);
+       writel(0x00000011, IO_ADDRESS(GEMINI_SATA_BASE) + 0x1c);
+
+       /* disabling port if no drive is present */
+       do
+       {
+               msleep(100);
+               phy_status = readb(IO_ADDRESS(GEMINI_SATA_BASE) + 0x08);
+               if (phy_status & 0x01) break;
+       } while (time_before(jiffies, timeout));
+       if (!(phy_status & 0x01))
+               writel(0x00, IO_ADDRESS(GEMINI_SATA_BASE) + 0x18);
+
+       do
+       {
+               msleep(100);
+               phy_status = readb(IO_ADDRESS(GEMINI_SATA_BASE) + 0x0C);
+               if (phy_status & 0x01) break;
+       } while (time_before(jiffies, timeout));
+       if (!(phy_status & 0x01))
+               writel(0x00, IO_ADDRESS(GEMINI_SATA_BASE) + 0x1C);
+
+       return platform_driver_register(&gemini_pata_driver);
+}
+
+static void __exit gemini_pata_module_exit(void)
+{
+       platform_driver_unregister(&gemini_pata_driver);
+}
+
+module_init(gemini_pata_module_init);
+module_exit(gemini_pata_module_exit);
+
+MODULE_AUTHOR("Janos Laube <[email protected]>");
+MODULE_DESCRIPTION("Parallel ATA driver for Gemini SoC's");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_NAME);
Index: target/linux/gemini/files/drivers/rtc/rtc-gemini.c
===================================================================
--- target/linux/gemini/files/drivers/rtc/rtc-gemini.c  (révision 0)
+++ target/linux/gemini/files/drivers/rtc/rtc-gemini.c  (révision 0)
@@ -0,0 +1,247 @@
+/*
+ *  Gemini OnChip RTC
+ *
+ *  Copyright (C) 2009 Janos Laube <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/rtc.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+
+#define GEMINI_RTC_EPOCH       1970
+
+static const unsigned char days_in_mo[] = {
+       0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+struct gemini_rtc
+{
+       struct rtc_device*      rtc_dev;
+       void __iomem*           rtc_base;
+       int                     rtc_irq;
+};
+
+enum e_gemini_rtc_offsets
+{
+       GEMINI_RTC_SECOND       = 0x00,
+       GEMINI_RTC_MINUTE       = 0x04,
+       GEMINI_RTC_HOUR         = 0x08,
+       GEMINI_RTC_DAYS         = 0x0C,
+       GEMINI_RTC_ALARM_SECOND = 0x10,
+       GEMINI_RTC_ALARM_MINUTE = 0x14,
+       GEMINI_RTC_ALARM_HOUR   = 0x18,
+       GEMINI_RTC_RECORD       = 0x1C,
+       GEMINI_RTC_CR           = 0x20
+};
+
+static irqreturn_t gemini_rtc_interrupt(int irq, void* dev)
+{
+       return IRQ_HANDLED;
+}
+
+static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct gemini_rtc* rtc = dev_get_drvdata(dev);
+
+       unsigned int  days;
+       unsigned int  months=0;
+       unsigned int  years;
+       unsigned int  hrs;
+       unsigned int  min;
+       unsigned int  sec;
+       unsigned int  rtc_record;
+       unsigned int  total_sec;
+       unsigned int  leap_year;
+       unsigned int  i;
+
+       sec = ioread32(rtc->rtc_base + GEMINI_RTC_SECOND);
+       min = ioread32(rtc->rtc_base + GEMINI_RTC_MINUTE);
+       hrs = ioread32(rtc->rtc_base + GEMINI_RTC_HOUR);
+       days = ioread32(rtc->rtc_base + GEMINI_RTC_DAYS);
+       rtc_record = ioread32(rtc->rtc_base + GEMINI_RTC_RECORD);
+
+       total_sec = rtc_record + days*86400 + hrs*3600 + min*60 + sec;
+
+       tm->tm_sec = total_sec % 60;
+       tm->tm_min = (total_sec/60) % 60;
+       tm->tm_hour = (total_sec/3600) % 24;
+
+       years = GEMINI_RTC_EPOCH;
+       days  = total_sec/86400;
+       while (days > 365)
+       {
+               leap_year = (!(years % 4) && (years % 100)) || !(years % 400);
+               days = days - 365 - leap_year;
+               years++;
+       }
+       leap_year = (!(years % 4) && (years + GEMINI_RTC_EPOCH % 100))
+                               || !(years % 400);
+
+       for (i=1;i<=12;i++)
+       {
+               if (days > (days_in_mo[i] + ((i == 2) && leap_year)))
+                       days = days - (days_in_mo[i] + ((i == 2) && leap_year));
+               else
+               {
+                       months = i;
+                       break;
+               }
+       }
+
+       tm->tm_mday = days+1;
+       tm->tm_mon  = months-1;
+       tm->tm_year = years-1900;
+       return 0;
+}
+
+static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct gemini_rtc* rtc = dev_get_drvdata(dev);
+       unsigned char mon, day, hrs, min, sec, leap_year;
+       unsigned int years;
+       unsigned int rtc_record;
+       unsigned int rtc_sec,rtc_min,rtc_hour,rtc_day,total_sec;
+
+       years = tm->tm_year + 1900;
+       mon = tm->tm_mon + 1;
+       day = tm->tm_mday;
+       hrs = tm->tm_hour;
+       min = tm->tm_min;
+       sec = tm->tm_sec;
+
+       if (years < GEMINI_RTC_EPOCH)
+               return -EINVAL;
+       if (years >= GEMINI_RTC_EPOCH + 178)
+               return -EINVAL;
+
+       leap_year = ((!(years % 4) && (years % 100)) || !(years % 400));
+       if ((mon > 12) || (day == 0))
+               return -EINVAL;
+       if (day > (days_in_mo[mon] + ((mon == 2) && leap_year)))
+               return -EINVAL;
+       if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+               return -EINVAL;
+
+       rtc_record = mktime(years,mon,day,hrs,min,sec);
+       rtc_record -= mktime(GEMINI_RTC_EPOCH, 1, 1, 0, 0, 0);
+
+       rtc_sec = ioread32(rtc->rtc_base + GEMINI_RTC_SECOND);
+       rtc_min = ioread32(rtc->rtc_base + GEMINI_RTC_MINUTE);
+       rtc_hour = ioread32(rtc->rtc_base + GEMINI_RTC_HOUR);
+       rtc_day = ioread32(rtc->rtc_base + GEMINI_RTC_DAYS);
+       total_sec= rtc_day*86400 + rtc_hour*3600 + rtc_min*60 + rtc_sec;
+
+       iowrite32(rtc_record - total_sec, rtc->rtc_base + GEMINI_RTC_RECORD);
+       iowrite32(0x01, rtc->rtc_base + GEMINI_RTC_CR);
+       return 0;
+}
+
+static struct rtc_class_ops gemini_rtc_ops = {
+       .read_time     = gemini_rtc_read_time,
+       .set_time      = gemini_rtc_set_time,
+};
+
+static int __devinit gemini_rtc_probe(struct platform_device *pdev)
+{
+       struct gemini_rtc *rtc;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int ret;
+
+       rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+       if (unlikely(!rtc))
+               return -ENOMEM;
+       platform_set_drvdata(pdev, rtc);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res)
+       {
+               ret = -ENODEV;
+               goto err;
+       }
+       rtc->rtc_irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+       {
+               ret = -ENODEV;
+               goto err;
+       }
+       rtc->rtc_base = devm_ioremap(&pdev->dev, res->start,
+               res->end - res->start + 1);
+
+       ret = request_irq(rtc->rtc_irq, gemini_rtc_interrupt,
+               IRQF_SHARED, pdev->name, dev);
+
+       if (unlikely(ret))
+               goto err;
+
+       rtc->rtc_dev = rtc_device_register(pdev->name, dev, &gemini_rtc_ops,
+               THIS_MODULE);
+       if (unlikely(IS_ERR(rtc->rtc_dev)))
+       {
+               ret = PTR_ERR(rtc->rtc_dev);
+               goto err;
+       }
+       return 0;
+
+ err:
+       kfree(rtc);
+       return ret;
+}
+
+static int __devexit gemini_rtc_remove(struct platform_device *pdev)
+{
+       struct gemini_rtc *rtc = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+
+       free_irq(rtc->rtc_irq, dev);
+       rtc_device_unregister(rtc->rtc_dev);
+       platform_set_drvdata(pdev, NULL);
+       kfree(rtc);
+
+       return 0;
+}
+
+static struct platform_driver gemini_rtc_driver = {
+       .driver         = {
+               .name   = "rtc-gemini",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = gemini_rtc_probe,
+       .remove         = __devexit_p(gemini_rtc_remove),
+};
+
+static int __init gemini_rtc_init(void)
+{
+       return platform_driver_register(&gemini_rtc_driver);
+}
+
+static void __exit gemini_rtc_exit(void)
+{
+       platform_driver_unregister(&gemini_rtc_driver);
+}
+
+module_init(gemini_rtc_init);
+module_exit(gemini_rtc_exit);
+
+MODULE_AUTHOR("Janos Laube <[email protected]>");
+MODULE_ALIAS("platform:rtc-gemini");
+MODULE_LICENSE("GPL");
Index: target/linux/gemini/files/drivers/misc/gemini_pwc.c
===================================================================
--- target/linux/gemini/files/drivers/misc/gemini_pwc.c (révision 0)
+++ target/linux/gemini/files/drivers/misc/gemini_pwc.c (révision 0)
@@ -0,0 +1,195 @@
+/*
+ *  Gemini Power Control driver
+ *
+ *  Copyright (C) 2009 Janos Laube <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+
+#define DRV_VERSION            0.1
+
+#define GEMINI_PWC_ID          0x00010500
+#define        GEMINI_PWC_IDREG        0x00
+#define        GEMINI_PWC_CTRLREG      0x04
+#define        GEMINI_PWC_STATREG      0x08
+#define        GEMINI_POWERBUTTON      0x40
+
+struct gemini_pwc
+{
+       void __iomem*   pwc_base;
+       int             pwc_irq;
+};
+
+static spinlock_t pwc_lock;
+/* work around bad designed hardware, the IB-4220-B powerbutton seems not
+ * to be debounced very well
+ */
+static int pwc_poweroff_issued = 0;
+
+static void gemini_power_off(void)
+{
+       unsigned int reg;
+       printk("gemini_pwc: power off\n");
+       reg = readl(IO_ADDRESS(GEMINI_POWER_CTRL_BASE) + GEMINI_PWC_CTRLREG);
+       writel(reg | BIT(2) | BIT(1),
+               IO_ADDRESS(GEMINI_POWER_CTRL_BASE) + GEMINI_PWC_CTRLREG);
+       reg &= ~BIT(1);
+       reg |= BIT(0);
+       writel(reg | BIT(2),
+               IO_ADDRESS(GEMINI_POWER_CTRL_BASE) + GEMINI_PWC_CTRLREG);
+}
+
+static irqreturn_t gemini_pwc_interrupt(int irq, void* dev)
+{
+       unsigned int reg, src;
+
+       spin_lock_irq(&pwc_lock);
+       /* clear pwc interrupt */
+       writel(readl(IO_ADDRESS(GEMINI_POWER_CTRL_BASE) + GEMINI_PWC_CTRLREG)
+               | (1 << 2), IO_ADDRESS(GEMINI_POWER_CTRL_BASE) +
+               GEMINI_PWC_CTRLREG);
+       reg = readl(IO_ADDRESS(GEMINI_INTERRUPT_BASE) + 0x08);
+       reg |= (1 << IRQ_PWR);
+       writel(reg, IO_ADDRESS(GEMINI_INTERRUPT_BASE) + 0x08);
+       barrier();
+
+       src = readl(IO_ADDRESS(GEMINI_POWER_CTRL_BASE) +
+               GEMINI_PWC_STATREG) & 0x70;
+       if ((src == GEMINI_POWERBUTTON) && (!pwc_poweroff_issued))
+       {
+               printk("gemini_pwc: shutting down machine\n");
+               orderly_poweroff(1);
+               pwc_poweroff_issued = 1;
+       }
+       spin_unlock_irq(&pwc_lock);
+       return IRQ_HANDLED;
+}
+
+static int __devinit gemini_pwc_probe(struct platform_device *pdev)
+{
+       struct gemini_pwc *pwc;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int ret;
+       unsigned int reg;
+
+       pwc = kzalloc(sizeof(struct gemini_pwc), GFP_KERNEL);
+       if (unlikely(!pwc))
+               return -ENOMEM;
+       platform_set_drvdata(pdev, pwc);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res)
+       {
+               ret = -ENODEV;
+               goto err;
+       }
+       pwc->pwc_irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+       {
+               ret = -ENODEV;
+               goto err;
+       }
+       pwc->pwc_base = devm_ioremap(&pdev->dev, res->start,
+               res->end - res->start + 1);
+
+       reg = readl(pwc->pwc_base + GEMINI_PWC_IDREG);
+       reg = reg & 0xFFFFFF00;
+       if (reg != GEMINI_PWC_ID)
+       {
+               ret = -ENODEV;
+               goto wrongid;
+       }
+
+       pm_power_off = gemini_power_off;
+
+       /* clear pwc interrupt */
+       writel(readl(pwc->pwc_base + GEMINI_PWC_CTRLREG)
+               | (1 << 2), pwc->pwc_base + GEMINI_PWC_CTRLREG);
+       reg = readl(IO_ADDRESS(GEMINI_INTERRUPT_BASE)+0x08);
+       reg |= (1 << IRQ_PWR);
+       writel(reg, IO_ADDRESS(GEMINI_INTERRUPT_BASE)+0x08);
+       barrier();
+       mdelay(1);
+
+       ret = request_irq(pwc->pwc_irq, gemini_pwc_interrupt, IRQF_DISABLED,
+               pdev->name, dev);
+       if (unlikely(ret))
+               goto err;
+
+       /* enable pwc device */
+       writel(readl(pwc->pwc_base + GEMINI_PWC_CTRLREG)
+                       | (1 << 1), pwc->pwc_base + GEMINI_PWC_CTRLREG);
+
+       return 0;
+
+wrongid:
+       printk("gemini_pwc: wrong PWC id\n");
+err:
+       kfree(pwc);
+       return ret;
+}
+
+static int __devexit gemini_pwc_remove(struct platform_device *pdev)
+{
+       struct gemini_pwc *pwc = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+
+       pm_power_off = 0;
+       free_irq(pwc->pwc_irq, dev);
+       platform_set_drvdata(pdev, NULL);
+       kfree(pwc);
+
+       return 0;
+}
+
+static struct platform_driver gemini_pwc_driver = {
+       .driver         = {
+               .name   = "gemini_pwc",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = gemini_pwc_probe,
+       .remove         = __devexit_p(gemini_pwc_remove),
+};
+
+static int __init gemini_pwc_init(void)
+{
+       return platform_driver_register(&gemini_pwc_driver);
+}
+
+static void __exit gemini_pwc_exit(void)
+{
+       platform_driver_unregister(&gemini_pwc_driver);
+}
+
+module_init(gemini_pwc_init);
+module_exit(gemini_pwc_exit);
+
+MODULE_AUTHOR("Janos Laube <[email protected]>");
+MODULE_ALIAS("platform:gemini_pwc");
+MODULE_DESCRIPTION("Driver for the Gemini Power Control device");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");



_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to