--- zzzz-none-000/linux-2.6.39.4/drivers/mmc/host/sdhci-pci.c 2011-08-03 19:43:28.000000000 +0000 +++ puma6-atom-6490-729/linux-2.6.39.4/drivers/mmc/host/sdhci-pci.c 2021-11-10 13:38:15.000000000 +0000 @@ -12,6 +12,15 @@ * - JMicron (hardware and technical support) */ + +/****************************************************************** + + Includes Intel Corporation's changes/modifications dated: 07/2012. + Changed/modified portions - Copyright(c) 2011-2012, Intel Corporation. + +******************************************************************/ + + #include #include #include @@ -23,7 +32,12 @@ #include #include - +#if defined(CONFIG_ARCH_GEN3) && defined(CONFIG_HW_MUTEXES) +#include +#if defined(CONFIG_CE_MAILBOX) +#include +#endif +#endif #include "sdhci.h" /* @@ -40,6 +54,11 @@ #define MAX_SLOTS 8 +#ifdef CONFIG_ARCH_GEN3 +int scan_thread_done = 0; +extern int intelce_boot_mode; +#endif + struct sdhci_pci_chip; struct sdhci_pci_slot; @@ -786,6 +805,90 @@ return 0; } +#ifdef CONFIG_ARCH_GEN3 +/* AEP only supports dword read and write */ +static inline u8 sdhci_puma6_readb(struct sdhci_host *host, int reg) +{ + int align_reg; + u32 align_val; + u8 val; + /* Align the register to 32 bit boundary */ + align_reg = reg & ~0x3; + align_val = readl(host->ioaddr + align_reg); + val = (align_val >> ((reg % 4) * 8)) & 0xFF; + + return val; +} + +static inline u16 sdhci_puma6_readw(struct sdhci_host *host, int reg) +{ + int align_reg; + u32 align_val; + u16 val; + /* Align the register to 32 bit boundary */ + align_reg = reg & ~0x3; + align_val = readl(host->ioaddr + align_reg); + val = (align_val >> ((reg % 4) * 8)) & 0xFFFF; + + return val; +} + +static inline void sdhci_puma6_writeb(struct sdhci_host *host, u8 val, int reg) +{ + int align_reg; + u32 align_val; + + /* Align the register to 32 bit boundary */ + align_reg = reg & ~0x3; + align_val = readl(host->ioaddr + align_reg); + + /* Clear wanted byte and set with new value */ + align_val = align_val &~ (0xFF << ((reg % 4) * 8)); + align_val = align_val | val << ((reg % 4) * 8); + writel(align_val, host->ioaddr + align_reg); +} + +static inline void sdhci_puma6_writew(struct sdhci_host *host, u16 val, int reg) +{ + int align_reg; + u32 align_val; + static u32 shadow_value; + static u32 shadow_valid = 0; + /* In Puma6, we must write to HC Registers within width of 32 bits (4 bytes alingment) + * Solution: Read the 32 bit register, modify the High Word (16 bit), or Low Word, and write back 32 bits + * + * A special case is wiriting to TRANSFER MODE register. + * Writing to TRANSFER MODE Register with Read/modify/write solution (as above), will trigger the + * COMMAND Register, to send a message to eMMC card. + * Solution: write the value of TRANSFER MODE to 'a shadow' register, and next time the user write + * to COMMAND Register, then write the shadow register to TRANSFER MODE register. + * + * Note: According to spec after each writing to TRANSFER MODE register, and user will also write + * to COMMAND Register. + */ + + /* Align the register to 32 bit boundary */ + align_reg = reg & ~0x3; + + /* Save Transfer Mode to a shadow register */ + if (unlikely(reg == SDHCI_TRANSFER_MODE)) { + shadow_value = val; + shadow_valid = 1; + return; + } + /* Restore the Transfer Mode from a shadow register */ + if (unlikely((reg == SDHCI_COMMAND) && (shadow_valid == 1))) { + align_val = shadow_value; + shadow_valid = 0; + } else + align_val = readl(host->ioaddr + align_reg); + /* Clear wanted word and set with new value */ + align_val = align_val &~ (0xFFFF << ((reg % 4) * 8)); + align_val = align_val | val << ((reg % 4) * 8); + + writel(align_val, host->ioaddr + align_reg); +} +#endif static struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, }; @@ -805,11 +908,15 @@ mmc_pm_flag_t slot_pm_flags; mmc_pm_flag_t pm_flags = 0; int i, ret; - + chip = pci_get_drvdata(pdev); if (!chip) return 0; +#ifdef CONFIG_ARCH_GEN3 + if (chip->quirks & SDHCI_QUIRK_NO_SUSPEND) + return 0; +#endif for (i = 0;i < chip->num_slots;i++) { slot = chip->slots[i]; if (!slot) @@ -865,6 +972,10 @@ if (!chip) return 0; +#ifdef CONFIG_ARCH_GEN3 + if (chip->quirks & SDHCI_QUIRK_NO_SUSPEND) + return 0; +#endif pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); ret = pci_enable_device(pdev); @@ -902,13 +1013,20 @@ * Device probing/removal * * * \*****************************************************************************/ - +extern void sync_printk(void); static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar) { struct sdhci_pci_slot *slot; struct sdhci_host *host; int ret; +#ifdef CONFIG_ARCH_GEN3 + int tmp; + int id; + int aep_region = 0; + struct pci_dev *tmp_dev = NULL; + struct pci_dev *aep_pdev = NULL; +#endif if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); @@ -948,6 +1066,43 @@ host->irq = pdev->irq; +#ifdef CONFIG_ARCH_GEN3 + /* Should not fail SDHCI probe, even if AEP is not supported */ + aep_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0956, NULL); + if (!aep_pdev) + dev_info(&pdev->dev, "AEP device is not found\n"); + else { + ret = pci_request_region(aep_pdev, bar, mmc_hostname(host->mmc)); + if (ret) + dev_info(&pdev->dev, "cannot request AEP region\n"); + else { + aep_region = 1; + host->aep_base = pci_ioremap_bar(aep_pdev, bar); + if (!host->aep_base) { + dev_info(&pdev->dev, "failed to remap AEP registers\n"); + pci_release_region(aep_pdev, bar); + aep_region = 0; + } + else { + if (readl(host->aep_base + PV_CONTROL) & PV_CNTL_AEP_EN) + host->aep_enabled = true; + else + host->aep_enabled = false; + } + } + pci_dev_put(aep_pdev); + } + /* Virtual emmc controller locates at offset 0x1000 of AEP base */ + if (host->aep_enabled) { + host->ioaddr = host->aep_base + 0x1000; + /* AEP only supports DWORD register access */ + sdhci_pci_ops.read_b = sdhci_puma6_readb; + sdhci_pci_ops.read_w = sdhci_puma6_readw; + sdhci_pci_ops.write_b = sdhci_puma6_writeb; + sdhci_pci_ops.write_w = sdhci_puma6_writew; + } else { +#endif + ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); if (ret) { dev_err(&pdev->dev, "cannot request region\n"); @@ -961,15 +1116,82 @@ goto release; } +#ifdef CONFIG_ARCH_GEN3 + } +#endif if (chip->fixes && chip->fixes->probe_slot) { ret = chip->fixes->probe_slot(slot); if (ret) goto unmap; } - host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; +#ifdef CONFIG_ARCH_GEN3 + intelce_get_soc_info(&id, NULL); + if (id != CE2600_SOC_DEVICE_ID) { + if(pdev->revision >= 0x2) + host->flags |= SDHCI_SUPPORT_DDR; + } +#endif +#if defined(CONFIG_ARCH_GEN3) && defined(CONFIG_HW_MUTEXES) + /* If there's HW Mutex controller exist, then we'll need to use HW Mutex to make sure exclusive controller access from different processors */ + tmp_dev = pci_get_device(0x8086, HW_MUTEX_DEV_ID,NULL); + if (tmp_dev) + { + host->flags |= SDHCI_SUPPORT_HW_MUTEX; + pci_dev_put(tmp_dev); + } + +#if 0 // AVM/TKL: we already synchronized kernel start up on event x_EVENT_KERNEL_STARTED +#if defined(CONFIG_CE_MAILBOX) + if (sdhci_host_has_HWMTX(host) && (intelce_boot_mode == 0)) { + /* Wait till ARM doesn't use eMMC in legacy mode */ + printk(KERN_INFO "waiting for eMMC legacy mode exit notification from NPCPU ... ...\n"); + for (;;) { + tmp = npcpu_appcpu_mbx_receive_event_notification(NPCPU_EVENT_EMMC_INIT_EXIT,NULL); + if (tmp) { + dev_err(&pdev->dev, "can not receive legacy mode exit notification from NPCPU, retrying ... \n"); + } + else + break; + } + tmp = npcpu_appcpu_mbx_send_ack(NPCPU_EVENT_EMMC_INIT_EXIT); + if (tmp) { + dev_err(&pdev->dev, "can not send NPCPU_EVENT_EMMC_INIT_EXIT ACK message to NPCPU \n"); + } + } +#endif /*CONFIG_CE_MAILBOX */ +#endif + + LOCK_EMMC_HW_MUTEX(host->mmc); + ret = sdhci_add_host(host); + if (intelce_boot_mode == 1) { + while (!scan_thread_done) { + schedule_timeout(10); + } + } + + UNLOCK_EMMC_HW_MUTEX(host->mmc); +#if defined(CONFIG_CE_MAILBOX) + if (sdhci_host_has_HWMTX(host) && (intelce_boot_mode == 0)) { + if (!ret) { + for (;;) { + printk(KERN_INFO "waiting for eMMC advanced mode exit notification from NPCPU ... ...\n"); + tmp = npcpu_appcpu_mbx_receive_event_notification(NPCPU_EVENT_EMMC_ADVANCE_INIT_EXIT,NULL); + if (tmp) { + dev_err(&pdev->dev, "can not receive advanced mode exit notification from NPCPU, retrying ... \n"); + } + else + break; + } + sync_printk(); + } + } +#endif /*CONFIG_CE_MAILBOX */ + +#else ret = sdhci_add_host(host); +#endif if (ret) goto remove; @@ -986,6 +1208,12 @@ pci_release_region(pdev, bar); free: +#ifdef CONFIG_ARCH_GEN3 + if (host->aep_base) + iounmap(host->aep_base); + if (aep_region) + pci_release_region(aep_pdev, bar); +#endif sdhci_free_host(host); return ERR_PTR(ret); @@ -1019,6 +1247,11 @@ u8 slots, first_bar; int ret, i; +#ifdef CONFIG_ARCH_GEN3 + unsigned int id; + + intelce_get_soc_info(&id, NULL); +#endif BUG_ON(pdev == NULL); BUG_ON(ent == NULL); @@ -1063,7 +1296,10 @@ if (chip->fixes) chip->quirks = chip->fixes->quirks; chip->num_slots = slots; - +#ifdef CONFIG_ARCH_GEN3 + if (CE2600_SOC_DEVICE_ID == id) + chip->quirks |= SDHCI_QUIRK_NO_SUSPEND; +#endif pci_set_drvdata(pdev, chip); if (chip->fixes && chip->fixes->probe) {