162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/mmc/host/via-sdmmc.c - VIA SD/MMC Card Reader driver 462306a36Sopenharmony_ci * Copyright (c) 2008, VIA Technologies Inc. All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/highmem.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/mmc/host.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DRV_NAME "via_sdmmc" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define PCI_DEVICE_ID_VIA_9530 0x9530 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define VIA_CRDR_SDC_OFF 0x200 2162306a36Sopenharmony_ci#define VIA_CRDR_DDMA_OFF 0x400 2262306a36Sopenharmony_ci#define VIA_CRDR_PCICTRL_OFF 0x600 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define VIA_CRDR_MIN_CLOCK 375000 2562306a36Sopenharmony_ci#define VIA_CRDR_MAX_CLOCK 48000000 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * PCI registers 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define VIA_CRDR_PCI_WORK_MODE 0x40 3262306a36Sopenharmony_ci#define VIA_CRDR_PCI_DBG_MODE 0x41 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * SDC MMIO Registers 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL 0x0 3962306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_START 0x01 4062306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_WRITE 0x04 4162306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_SINGLE_WR 0x10 4262306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_SINGLE_RD 0x20 4362306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_MULTI_WR 0x30 4462306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_MULTI_RD 0x40 4562306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_STOP 0x70 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_RSP_NONE 0x0 4862306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_RSP_R1 0x10000 4962306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_RSP_R2 0x20000 5062306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_RSP_R3 0x30000 5162306a36Sopenharmony_ci#define VIA_CRDR_SDCTRL_RSP_R1B 0x90000 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define VIA_CRDR_SDCARG 0x4 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define VIA_CRDR_SDBUSMODE 0x8 5662306a36Sopenharmony_ci#define VIA_CRDR_SDMODE_4BIT 0x02 5762306a36Sopenharmony_ci#define VIA_CRDR_SDMODE_CLK_ON 0x40 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define VIA_CRDR_SDBLKLEN 0xc 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Bit 0 -Bit 10 : Block length. So, the maximum block length should be 2048. 6262306a36Sopenharmony_ci * Bit 11 - Bit 13 : Reserved. 6362306a36Sopenharmony_ci * GPIDET : Select GPI pin to detect card, GPI means CR_CD# in top design. 6462306a36Sopenharmony_ci * INTEN : Enable SD host interrupt. 6562306a36Sopenharmony_ci * Bit 16 - Bit 31 : Block count. So, the maximun block count should be 65536. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define VIA_CRDR_SDBLKLEN_GPIDET 0x2000 6862306a36Sopenharmony_ci#define VIA_CRDR_SDBLKLEN_INTEN 0x8000 6962306a36Sopenharmony_ci#define VIA_CRDR_MAX_BLOCK_COUNT 65536 7062306a36Sopenharmony_ci#define VIA_CRDR_MAX_BLOCK_LENGTH 2048 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define VIA_CRDR_SDRESP0 0x10 7362306a36Sopenharmony_ci#define VIA_CRDR_SDRESP1 0x14 7462306a36Sopenharmony_ci#define VIA_CRDR_SDRESP2 0x18 7562306a36Sopenharmony_ci#define VIA_CRDR_SDRESP3 0x1c 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define VIA_CRDR_SDCURBLKCNT 0x20 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK 0x24 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * MBDIE : Multiple Blocks transfer Done Interrupt Enable 8262306a36Sopenharmony_ci * BDDIE : Block Data transfer Done Interrupt Enable 8362306a36Sopenharmony_ci * CIRIE : Card Insertion or Removal Interrupt Enable 8462306a36Sopenharmony_ci * CRDIE : Command-Response transfer Done Interrupt Enable 8562306a36Sopenharmony_ci * CRTOIE : Command-Response response TimeOut Interrupt Enable 8662306a36Sopenharmony_ci * ASCRDIE : Auto Stop Command-Response transfer Done Interrupt Enable 8762306a36Sopenharmony_ci * DTIE : Data access Timeout Interrupt Enable 8862306a36Sopenharmony_ci * SCIE : reSponse CRC error Interrupt Enable 8962306a36Sopenharmony_ci * RCIE : Read data CRC error Interrupt Enable 9062306a36Sopenharmony_ci * WCIE : Write data CRC error Interrupt Enable 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_MBDIE 0x10 9362306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_BDDIE 0x20 9462306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_CIRIE 0x80 9562306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_CRDIE 0x200 9662306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_CRTOIE 0x400 9762306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_ASCRDIE 0x800 9862306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_DTIE 0x1000 9962306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_SCIE 0x2000 10062306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_RCIE 0x4000 10162306a36Sopenharmony_ci#define VIA_CRDR_SDINTMASK_WCIE 0x8000 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define VIA_CRDR_SDACTIVE_INTMASK \ 10462306a36Sopenharmony_ci (VIA_CRDR_SDINTMASK_MBDIE | VIA_CRDR_SDINTMASK_CIRIE \ 10562306a36Sopenharmony_ci | VIA_CRDR_SDINTMASK_CRDIE | VIA_CRDR_SDINTMASK_CRTOIE \ 10662306a36Sopenharmony_ci | VIA_CRDR_SDINTMASK_DTIE | VIA_CRDR_SDINTMASK_SCIE \ 10762306a36Sopenharmony_ci | VIA_CRDR_SDINTMASK_RCIE | VIA_CRDR_SDINTMASK_WCIE) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define VIA_CRDR_SDSTATUS 0x28 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * CECC : Reserved 11262306a36Sopenharmony_ci * WP : SD card Write Protect status 11362306a36Sopenharmony_ci * SLOTD : Reserved 11462306a36Sopenharmony_ci * SLOTG : SD SLOT status(Gpi pin status) 11562306a36Sopenharmony_ci * MBD : Multiple Blocks transfer Done interrupt status 11662306a36Sopenharmony_ci * BDD : Block Data transfer Done interrupt status 11762306a36Sopenharmony_ci * CD : Reserved 11862306a36Sopenharmony_ci * CIR : Card Insertion or Removal interrupt detected on GPI pin 11962306a36Sopenharmony_ci * IO : Reserved 12062306a36Sopenharmony_ci * CRD : Command-Response transfer Done interrupt status 12162306a36Sopenharmony_ci * CRTO : Command-Response response TimeOut interrupt status 12262306a36Sopenharmony_ci * ASCRDIE : Auto Stop Command-Response transfer Done interrupt status 12362306a36Sopenharmony_ci * DT : Data access Timeout interrupt status 12462306a36Sopenharmony_ci * SC : reSponse CRC error interrupt status 12562306a36Sopenharmony_ci * RC : Read data CRC error interrupt status 12662306a36Sopenharmony_ci * WC : Write data CRC error interrupt status 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CECC 0x01 12962306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_WP 0x02 13062306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_SLOTD 0x04 13162306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_SLOTG 0x08 13262306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_MBD 0x10 13362306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_BDD 0x20 13462306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CD 0x40 13562306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CIR 0x80 13662306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_IO 0x100 13762306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CRD 0x200 13862306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CRTO 0x400 13962306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_ASCRDIE 0x800 14062306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_DT 0x1000 14162306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_SC 0x2000 14262306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_RC 0x4000 14362306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_WC 0x8000 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_IGN_MASK\ 14662306a36Sopenharmony_ci (VIA_CRDR_SDSTS_BDD | VIA_CRDR_SDSTS_ASCRDIE | VIA_CRDR_SDSTS_IO) 14762306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_INT_MASK \ 14862306a36Sopenharmony_ci (VIA_CRDR_SDSTS_MBD | VIA_CRDR_SDSTS_BDD | VIA_CRDR_SDSTS_CD \ 14962306a36Sopenharmony_ci | VIA_CRDR_SDSTS_CIR | VIA_CRDR_SDSTS_IO | VIA_CRDR_SDSTS_CRD \ 15062306a36Sopenharmony_ci | VIA_CRDR_SDSTS_CRTO | VIA_CRDR_SDSTS_ASCRDIE | VIA_CRDR_SDSTS_DT \ 15162306a36Sopenharmony_ci | VIA_CRDR_SDSTS_SC | VIA_CRDR_SDSTS_RC | VIA_CRDR_SDSTS_WC) 15262306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_W1C_MASK \ 15362306a36Sopenharmony_ci (VIA_CRDR_SDSTS_CECC | VIA_CRDR_SDSTS_MBD | VIA_CRDR_SDSTS_BDD \ 15462306a36Sopenharmony_ci | VIA_CRDR_SDSTS_CD | VIA_CRDR_SDSTS_CIR | VIA_CRDR_SDSTS_CRD \ 15562306a36Sopenharmony_ci | VIA_CRDR_SDSTS_CRTO | VIA_CRDR_SDSTS_ASCRDIE | VIA_CRDR_SDSTS_DT \ 15662306a36Sopenharmony_ci | VIA_CRDR_SDSTS_SC | VIA_CRDR_SDSTS_RC | VIA_CRDR_SDSTS_WC) 15762306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CMD_MASK \ 15862306a36Sopenharmony_ci (VIA_CRDR_SDSTS_CRD | VIA_CRDR_SDSTS_CRTO | VIA_CRDR_SDSTS_SC) 15962306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_DATA_MASK\ 16062306a36Sopenharmony_ci (VIA_CRDR_SDSTS_MBD | VIA_CRDR_SDSTS_DT \ 16162306a36Sopenharmony_ci | VIA_CRDR_SDSTS_RC | VIA_CRDR_SDSTS_WC) 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define VIA_CRDR_SDSTATUS2 0x2a 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * CFE : Enable SD host automatic Clock FReezing 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci#define VIA_CRDR_SDSTS_CFE 0x80 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define VIA_CRDR_SDRSPTMO 0x2C 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#define VIA_CRDR_SDCLKSEL 0x30 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define VIA_CRDR_SDEXTCTRL 0x34 17462306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_AUTOSTOP_SD 0x01 17562306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_SHIFT_9 0x02 17662306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_MMC_8BIT 0x04 17762306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_RELD_BLK 0x08 17862306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_BAD_CMDA 0x10 17962306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_BAD_DATA 0x20 18062306a36Sopenharmony_ci#define VIS_CRDR_SDEXTCTRL_AUTOSTOP_SPI 0x40 18162306a36Sopenharmony_ci#define VIA_CRDR_SDEXTCTRL_HISPD 0x80 18262306a36Sopenharmony_ci/* 0x38-0xFF reserved */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * Data DMA Control Registers 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#define VIA_CRDR_DMABASEADD 0x0 18962306a36Sopenharmony_ci#define VIA_CRDR_DMACOUNTER 0x4 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#define VIA_CRDR_DMACTRL 0x8 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * DIR :Transaction Direction 19462306a36Sopenharmony_ci * 0 : From card to memory 19562306a36Sopenharmony_ci * 1 : From memory to card 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci#define VIA_CRDR_DMACTRL_DIR 0x100 19862306a36Sopenharmony_ci#define VIA_CRDR_DMACTRL_ENIRQ 0x10000 19962306a36Sopenharmony_ci#define VIA_CRDR_DMACTRL_SFTRST 0x1000000 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define VIA_CRDR_DMASTS 0xc 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci#define VIA_CRDR_DMASTART 0x10 20462306a36Sopenharmony_ci/*0x14-0xFF reserved*/ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * PCI Control Registers 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/*0x0 - 0x1 reserved*/ 21162306a36Sopenharmony_ci#define VIA_CRDR_PCICLKGATT 0x2 21262306a36Sopenharmony_ci/* 21362306a36Sopenharmony_ci * SFTRST : 21462306a36Sopenharmony_ci * 0 : Soft reset all the controller and it will be de-asserted automatically 21562306a36Sopenharmony_ci * 1 : Soft reset is de-asserted 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci#define VIA_CRDR_PCICLKGATT_SFTRST 0x01 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * 3V3 : Pad power select 22062306a36Sopenharmony_ci * 0 : 1.8V 22162306a36Sopenharmony_ci * 1 : 3.3V 22262306a36Sopenharmony_ci * NOTE : No mater what the actual value should be, this bit always 22362306a36Sopenharmony_ci * read as 0. This is a hardware bug. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci#define VIA_CRDR_PCICLKGATT_3V3 0x10 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * PAD_PWRON : Pad Power on/off select 22862306a36Sopenharmony_ci * 0 : Power off 22962306a36Sopenharmony_ci * 1 : Power on 23062306a36Sopenharmony_ci * NOTE : No mater what the actual value should be, this bit always 23162306a36Sopenharmony_ci * read as 0. This is a hardware bug. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci#define VIA_CRDR_PCICLKGATT_PAD_PWRON 0x20 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define VIA_CRDR_PCISDCCLK 0x5 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define VIA_CRDR_PCIDMACLK 0x7 23862306a36Sopenharmony_ci#define VIA_CRDR_PCIDMACLK_SDC 0x2 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define VIA_CRDR_PCIINTCTRL 0x8 24162306a36Sopenharmony_ci#define VIA_CRDR_PCIINTCTRL_SDCIRQEN 0x04 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci#define VIA_CRDR_PCIINTSTATUS 0x9 24462306a36Sopenharmony_ci#define VIA_CRDR_PCIINTSTATUS_SDC 0x04 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL 0xa 24762306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_NO 0x0 24862306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_32US 0x1 24962306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_256US 0x2 25062306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_1024US 0x3 25162306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_256MS 0x4 25262306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_512MS 0x5 25362306a36Sopenharmony_ci#define VIA_CRDR_PCITMOCTRL_1024MS 0x6 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/*0xB-0xFF reserved*/ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cienum PCI_HOST_CLK_CONTROL { 25862306a36Sopenharmony_ci PCI_CLK_375K = 0x03, 25962306a36Sopenharmony_ci PCI_CLK_8M = 0x04, 26062306a36Sopenharmony_ci PCI_CLK_12M = 0x00, 26162306a36Sopenharmony_ci PCI_CLK_16M = 0x05, 26262306a36Sopenharmony_ci PCI_CLK_24M = 0x01, 26362306a36Sopenharmony_ci PCI_CLK_33M = 0x06, 26462306a36Sopenharmony_ci PCI_CLK_48M = 0x02 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct sdhcreg { 26862306a36Sopenharmony_ci u32 sdcontrol_reg; 26962306a36Sopenharmony_ci u32 sdcmdarg_reg; 27062306a36Sopenharmony_ci u32 sdbusmode_reg; 27162306a36Sopenharmony_ci u32 sdblklen_reg; 27262306a36Sopenharmony_ci u32 sdresp_reg[4]; 27362306a36Sopenharmony_ci u32 sdcurblkcnt_reg; 27462306a36Sopenharmony_ci u32 sdintmask_reg; 27562306a36Sopenharmony_ci u32 sdstatus_reg; 27662306a36Sopenharmony_ci u32 sdrsptmo_reg; 27762306a36Sopenharmony_ci u32 sdclksel_reg; 27862306a36Sopenharmony_ci u32 sdextctrl_reg; 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistruct pcictrlreg { 28262306a36Sopenharmony_ci u8 reserve[2]; 28362306a36Sopenharmony_ci u8 pciclkgat_reg; 28462306a36Sopenharmony_ci u8 pcinfcclk_reg; 28562306a36Sopenharmony_ci u8 pcimscclk_reg; 28662306a36Sopenharmony_ci u8 pcisdclk_reg; 28762306a36Sopenharmony_ci u8 pcicaclk_reg; 28862306a36Sopenharmony_ci u8 pcidmaclk_reg; 28962306a36Sopenharmony_ci u8 pciintctrl_reg; 29062306a36Sopenharmony_ci u8 pciintstatus_reg; 29162306a36Sopenharmony_ci u8 pcitmoctrl_reg; 29262306a36Sopenharmony_ci u8 Resv; 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistruct via_crdr_mmc_host { 29662306a36Sopenharmony_ci struct mmc_host *mmc; 29762306a36Sopenharmony_ci struct mmc_request *mrq; 29862306a36Sopenharmony_ci struct mmc_command *cmd; 29962306a36Sopenharmony_ci struct mmc_data *data; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci void __iomem *mmiobase; 30262306a36Sopenharmony_ci void __iomem *sdhc_mmiobase; 30362306a36Sopenharmony_ci void __iomem *ddma_mmiobase; 30462306a36Sopenharmony_ci void __iomem *pcictrl_mmiobase; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci struct pcictrlreg pm_pcictrl_reg; 30762306a36Sopenharmony_ci struct sdhcreg pm_sdhc_reg; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci struct work_struct carddet_work; 31062306a36Sopenharmony_ci struct tasklet_struct finish_tasklet; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci struct timer_list timer; 31362306a36Sopenharmony_ci spinlock_t lock; 31462306a36Sopenharmony_ci u8 power; 31562306a36Sopenharmony_ci int reject; 31662306a36Sopenharmony_ci unsigned int quirks; 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* some devices need a very long delay for power to stabilize */ 32062306a36Sopenharmony_ci#define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define VIA_CMD_TIMEOUT_MS 1000 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic const struct pci_device_id via_ids[] = { 32562306a36Sopenharmony_ci {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530, 32662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, 32762306a36Sopenharmony_ci {0,} 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, via_ids); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void via_print_sdchc(struct via_crdr_mmc_host *host) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci void __iomem *addrbase = host->sdhc_mmiobase; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci pr_debug("SDC MMIO Registers:\n"); 33762306a36Sopenharmony_ci pr_debug("SDCONTROL=%08x, SDCMDARG=%08x, SDBUSMODE=%08x\n", 33862306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDCTRL), 33962306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDCARG), 34062306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDBUSMODE)); 34162306a36Sopenharmony_ci pr_debug("SDBLKLEN=%08x, SDCURBLKCNT=%08x, SDINTMASK=%08x\n", 34262306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDBLKLEN), 34362306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDCURBLKCNT), 34462306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDINTMASK)); 34562306a36Sopenharmony_ci pr_debug("SDSTATUS=%08x, SDCLKSEL=%08x, SDEXTCTRL=%08x\n", 34662306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDSTATUS), 34762306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDCLKSEL), 34862306a36Sopenharmony_ci readl(addrbase + VIA_CRDR_SDEXTCTRL)); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void via_print_pcictrl(struct via_crdr_mmc_host *host) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci void __iomem *addrbase = host->pcictrl_mmiobase; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci pr_debug("PCI Control Registers:\n"); 35662306a36Sopenharmony_ci pr_debug("PCICLKGATT=%02x, PCISDCCLK=%02x, PCIDMACLK=%02x\n", 35762306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCICLKGATT), 35862306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCISDCCLK), 35962306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCIDMACLK)); 36062306a36Sopenharmony_ci pr_debug("PCIINTCTRL=%02x, PCIINTSTATUS=%02x\n", 36162306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCIINTCTRL), 36262306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCIINTSTATUS)); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void via_save_pcictrlreg(struct via_crdr_mmc_host *host) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct pcictrlreg *pm_pcictrl_reg; 36862306a36Sopenharmony_ci void __iomem *addrbase; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci pm_pcictrl_reg = &(host->pm_pcictrl_reg); 37162306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci pm_pcictrl_reg->pciclkgat_reg = readb(addrbase + VIA_CRDR_PCICLKGATT); 37462306a36Sopenharmony_ci pm_pcictrl_reg->pciclkgat_reg |= 37562306a36Sopenharmony_ci VIA_CRDR_PCICLKGATT_3V3 | VIA_CRDR_PCICLKGATT_PAD_PWRON; 37662306a36Sopenharmony_ci pm_pcictrl_reg->pcisdclk_reg = readb(addrbase + VIA_CRDR_PCISDCCLK); 37762306a36Sopenharmony_ci pm_pcictrl_reg->pcidmaclk_reg = readb(addrbase + VIA_CRDR_PCIDMACLK); 37862306a36Sopenharmony_ci pm_pcictrl_reg->pciintctrl_reg = readb(addrbase + VIA_CRDR_PCIINTCTRL); 37962306a36Sopenharmony_ci pm_pcictrl_reg->pciintstatus_reg = 38062306a36Sopenharmony_ci readb(addrbase + VIA_CRDR_PCIINTSTATUS); 38162306a36Sopenharmony_ci pm_pcictrl_reg->pcitmoctrl_reg = readb(addrbase + VIA_CRDR_PCITMOCTRL); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void via_restore_pcictrlreg(struct via_crdr_mmc_host *host) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct pcictrlreg *pm_pcictrl_reg; 38762306a36Sopenharmony_ci void __iomem *addrbase; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci pm_pcictrl_reg = &(host->pm_pcictrl_reg); 39062306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pciclkgat_reg, addrbase + VIA_CRDR_PCICLKGATT); 39362306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pcisdclk_reg, addrbase + VIA_CRDR_PCISDCCLK); 39462306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pcidmaclk_reg, addrbase + VIA_CRDR_PCIDMACLK); 39562306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pciintctrl_reg, addrbase + VIA_CRDR_PCIINTCTRL); 39662306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pciintstatus_reg, 39762306a36Sopenharmony_ci addrbase + VIA_CRDR_PCIINTSTATUS); 39862306a36Sopenharmony_ci writeb(pm_pcictrl_reg->pcitmoctrl_reg, addrbase + VIA_CRDR_PCITMOCTRL); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void via_save_sdcreg(struct via_crdr_mmc_host *host) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct sdhcreg *pm_sdhc_reg; 40462306a36Sopenharmony_ci void __iomem *addrbase; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci pm_sdhc_reg = &(host->pm_sdhc_reg); 40762306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci pm_sdhc_reg->sdcontrol_reg = readl(addrbase + VIA_CRDR_SDCTRL); 41062306a36Sopenharmony_ci pm_sdhc_reg->sdcmdarg_reg = readl(addrbase + VIA_CRDR_SDCARG); 41162306a36Sopenharmony_ci pm_sdhc_reg->sdbusmode_reg = readl(addrbase + VIA_CRDR_SDBUSMODE); 41262306a36Sopenharmony_ci pm_sdhc_reg->sdblklen_reg = readl(addrbase + VIA_CRDR_SDBLKLEN); 41362306a36Sopenharmony_ci pm_sdhc_reg->sdcurblkcnt_reg = readl(addrbase + VIA_CRDR_SDCURBLKCNT); 41462306a36Sopenharmony_ci pm_sdhc_reg->sdintmask_reg = readl(addrbase + VIA_CRDR_SDINTMASK); 41562306a36Sopenharmony_ci pm_sdhc_reg->sdstatus_reg = readl(addrbase + VIA_CRDR_SDSTATUS); 41662306a36Sopenharmony_ci pm_sdhc_reg->sdrsptmo_reg = readl(addrbase + VIA_CRDR_SDRSPTMO); 41762306a36Sopenharmony_ci pm_sdhc_reg->sdclksel_reg = readl(addrbase + VIA_CRDR_SDCLKSEL); 41862306a36Sopenharmony_ci pm_sdhc_reg->sdextctrl_reg = readl(addrbase + VIA_CRDR_SDEXTCTRL); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void via_restore_sdcreg(struct via_crdr_mmc_host *host) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct sdhcreg *pm_sdhc_reg; 42462306a36Sopenharmony_ci void __iomem *addrbase; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci pm_sdhc_reg = &(host->pm_sdhc_reg); 42762306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci writel(pm_sdhc_reg->sdcontrol_reg, addrbase + VIA_CRDR_SDCTRL); 43062306a36Sopenharmony_ci writel(pm_sdhc_reg->sdcmdarg_reg, addrbase + VIA_CRDR_SDCARG); 43162306a36Sopenharmony_ci writel(pm_sdhc_reg->sdbusmode_reg, addrbase + VIA_CRDR_SDBUSMODE); 43262306a36Sopenharmony_ci writel(pm_sdhc_reg->sdblklen_reg, addrbase + VIA_CRDR_SDBLKLEN); 43362306a36Sopenharmony_ci writel(pm_sdhc_reg->sdcurblkcnt_reg, addrbase + VIA_CRDR_SDCURBLKCNT); 43462306a36Sopenharmony_ci writel(pm_sdhc_reg->sdintmask_reg, addrbase + VIA_CRDR_SDINTMASK); 43562306a36Sopenharmony_ci writel(pm_sdhc_reg->sdstatus_reg, addrbase + VIA_CRDR_SDSTATUS); 43662306a36Sopenharmony_ci writel(pm_sdhc_reg->sdrsptmo_reg, addrbase + VIA_CRDR_SDRSPTMO); 43762306a36Sopenharmony_ci writel(pm_sdhc_reg->sdclksel_reg, addrbase + VIA_CRDR_SDCLKSEL); 43862306a36Sopenharmony_ci writel(pm_sdhc_reg->sdextctrl_reg, addrbase + VIA_CRDR_SDEXTCTRL); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic void via_pwron_sleep(struct via_crdr_mmc_host *sdhost) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci if (sdhost->quirks & VIA_CRDR_QUIRK_300MS_PWRDELAY) 44462306a36Sopenharmony_ci msleep(300); 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci msleep(3); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void via_set_ddma(struct via_crdr_mmc_host *host, 45062306a36Sopenharmony_ci dma_addr_t dmaaddr, u32 count, int dir, int enirq) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci void __iomem *addrbase; 45362306a36Sopenharmony_ci u32 ctrl_data = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (enirq) 45662306a36Sopenharmony_ci ctrl_data |= VIA_CRDR_DMACTRL_ENIRQ; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (dir) 45962306a36Sopenharmony_ci ctrl_data |= VIA_CRDR_DMACTRL_DIR; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci addrbase = host->ddma_mmiobase; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci writel(dmaaddr, addrbase + VIA_CRDR_DMABASEADD); 46462306a36Sopenharmony_ci writel(count, addrbase + VIA_CRDR_DMACOUNTER); 46562306a36Sopenharmony_ci writel(ctrl_data, addrbase + VIA_CRDR_DMACTRL); 46662306a36Sopenharmony_ci writel(0x01, addrbase + VIA_CRDR_DMASTART); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* It seems that our DMA can not work normally with 375kHz clock */ 46962306a36Sopenharmony_ci /* FIXME: don't brute-force 8MHz but use PIO at 375kHz !! */ 47062306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 47162306a36Sopenharmony_ci if (readb(addrbase + VIA_CRDR_PCISDCCLK) == PCI_CLK_375K) { 47262306a36Sopenharmony_ci dev_info(host->mmc->parent, "forcing card speed to 8MHz\n"); 47362306a36Sopenharmony_ci writeb(PCI_CLK_8M, addrbase + VIA_CRDR_PCISDCCLK); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void via_sdc_preparedata(struct via_crdr_mmc_host *host, 47862306a36Sopenharmony_ci struct mmc_data *data) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci void __iomem *addrbase; 48162306a36Sopenharmony_ci u32 blk_reg; 48262306a36Sopenharmony_ci int count; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci WARN_ON(host->data); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Sanity checks */ 48762306a36Sopenharmony_ci BUG_ON(data->blksz > host->mmc->max_blk_size); 48862306a36Sopenharmony_ci BUG_ON(data->blocks > host->mmc->max_blk_count); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci host->data = data; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, 49362306a36Sopenharmony_ci ((data->flags & MMC_DATA_READ) ? 49462306a36Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE)); 49562306a36Sopenharmony_ci BUG_ON(count != 1); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci via_set_ddma(host, sg_dma_address(data->sg), sg_dma_len(data->sg), 49862306a36Sopenharmony_ci (data->flags & MMC_DATA_WRITE) ? 1 : 0, 1); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci blk_reg = data->blksz - 1; 50362306a36Sopenharmony_ci blk_reg |= VIA_CRDR_SDBLKLEN_GPIDET | VIA_CRDR_SDBLKLEN_INTEN; 50462306a36Sopenharmony_ci blk_reg |= (data->blocks) << 16; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci writel(blk_reg, addrbase + VIA_CRDR_SDBLKLEN); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void via_sdc_get_response(struct via_crdr_mmc_host *host, 51062306a36Sopenharmony_ci struct mmc_command *cmd) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci void __iomem *addrbase = host->sdhc_mmiobase; 51362306a36Sopenharmony_ci u32 dwdata0 = readl(addrbase + VIA_CRDR_SDRESP0); 51462306a36Sopenharmony_ci u32 dwdata1 = readl(addrbase + VIA_CRDR_SDRESP1); 51562306a36Sopenharmony_ci u32 dwdata2 = readl(addrbase + VIA_CRDR_SDRESP2); 51662306a36Sopenharmony_ci u32 dwdata3 = readl(addrbase + VIA_CRDR_SDRESP3); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 51962306a36Sopenharmony_ci cmd->resp[0] = ((u8) (dwdata1)) | 52062306a36Sopenharmony_ci (((u8) (dwdata0 >> 24)) << 8) | 52162306a36Sopenharmony_ci (((u8) (dwdata0 >> 16)) << 16) | 52262306a36Sopenharmony_ci (((u8) (dwdata0 >> 8)) << 24); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci cmd->resp[1] = ((u8) (dwdata2)) | 52562306a36Sopenharmony_ci (((u8) (dwdata1 >> 24)) << 8) | 52662306a36Sopenharmony_ci (((u8) (dwdata1 >> 16)) << 16) | 52762306a36Sopenharmony_ci (((u8) (dwdata1 >> 8)) << 24); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci cmd->resp[2] = ((u8) (dwdata3)) | 53062306a36Sopenharmony_ci (((u8) (dwdata2 >> 24)) << 8) | 53162306a36Sopenharmony_ci (((u8) (dwdata2 >> 16)) << 16) | 53262306a36Sopenharmony_ci (((u8) (dwdata2 >> 8)) << 24); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci cmd->resp[3] = 0xff | 53562306a36Sopenharmony_ci ((((u8) (dwdata3 >> 24))) << 8) | 53662306a36Sopenharmony_ci (((u8) (dwdata3 >> 16)) << 16) | 53762306a36Sopenharmony_ci (((u8) (dwdata3 >> 8)) << 24); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci dwdata0 >>= 8; 54062306a36Sopenharmony_ci cmd->resp[0] = ((dwdata0 & 0xff) << 24) | 54162306a36Sopenharmony_ci (((dwdata0 >> 8) & 0xff) << 16) | 54262306a36Sopenharmony_ci (((dwdata0 >> 16) & 0xff) << 8) | (dwdata1 & 0xff); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dwdata1 >>= 8; 54562306a36Sopenharmony_ci cmd->resp[1] = ((dwdata1 & 0xff) << 24) | 54662306a36Sopenharmony_ci (((dwdata1 >> 8) & 0xff) << 16) | 54762306a36Sopenharmony_ci (((dwdata1 >> 16) & 0xff) << 8); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void via_sdc_send_command(struct via_crdr_mmc_host *host, 55262306a36Sopenharmony_ci struct mmc_command *cmd) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci void __iomem *addrbase; 55562306a36Sopenharmony_ci struct mmc_data *data; 55662306a36Sopenharmony_ci unsigned int timeout_ms; 55762306a36Sopenharmony_ci u32 cmdctrl = 0; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci WARN_ON(host->cmd); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci data = cmd->data; 56262306a36Sopenharmony_ci host->cmd = cmd; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : VIA_CMD_TIMEOUT_MS; 56562306a36Sopenharmony_ci mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms)); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /*Command index*/ 56862306a36Sopenharmony_ci cmdctrl = cmd->opcode << 8; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /*Response type*/ 57162306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 57262306a36Sopenharmony_ci case MMC_RSP_NONE: 57362306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_RSP_NONE; 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci case MMC_RSP_R1: 57662306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_RSP_R1; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci case MMC_RSP_R1B: 57962306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_RSP_R1B; 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci case MMC_RSP_R2: 58262306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_RSP_R2; 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci case MMC_RSP_R3: 58562306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_RSP_R3; 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci pr_err("%s: cmd->flag is not valid\n", mmc_hostname(host->mmc)); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (!(cmd->data)) 59362306a36Sopenharmony_ci goto nodata; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci via_sdc_preparedata(host, data); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /*Command control*/ 59862306a36Sopenharmony_ci if (data->blocks > 1) { 59962306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 60062306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_WRITE; 60162306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_MULTI_WR; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_MULTI_RD; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 60762306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_WRITE; 60862306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_SINGLE_WR; 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_SINGLE_RD; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cinodata: 61562306a36Sopenharmony_ci if (cmd == host->mrq->stop) 61662306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_STOP; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci cmdctrl |= VIA_CRDR_SDCTRL_START; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 62162306a36Sopenharmony_ci writel(cmd->arg, addrbase + VIA_CRDR_SDCARG); 62262306a36Sopenharmony_ci writel(cmdctrl, addrbase + VIA_CRDR_SDCTRL); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void via_sdc_finish_data(struct via_crdr_mmc_host *host) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct mmc_data *data; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci BUG_ON(!host->data); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci data = host->data; 63262306a36Sopenharmony_ci host->data = NULL; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (data->error) 63562306a36Sopenharmony_ci data->bytes_xfered = 0; 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, 64062306a36Sopenharmony_ci ((data->flags & MMC_DATA_READ) ? 64162306a36Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE)); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (data->stop) 64462306a36Sopenharmony_ci via_sdc_send_command(host, data->stop); 64562306a36Sopenharmony_ci else 64662306a36Sopenharmony_ci tasklet_schedule(&host->finish_tasklet); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void via_sdc_finish_command(struct via_crdr_mmc_host *host) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci via_sdc_get_response(host, host->cmd); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci host->cmd->error = 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (!host->cmd->data) 65662306a36Sopenharmony_ci tasklet_schedule(&host->finish_tasklet); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci host->cmd = NULL; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci void __iomem *addrbase; 66462306a36Sopenharmony_ci struct via_crdr_mmc_host *host; 66562306a36Sopenharmony_ci unsigned long flags; 66662306a36Sopenharmony_ci u16 status; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci host = mmc_priv(mmc); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 67362306a36Sopenharmony_ci writeb(VIA_CRDR_PCIDMACLK_SDC, addrbase + VIA_CRDR_PCIDMACLK); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS); 67662306a36Sopenharmony_ci status &= VIA_CRDR_SDSTS_W1C_MASK; 67762306a36Sopenharmony_ci writew(status, host->sdhc_mmiobase + VIA_CRDR_SDSTATUS); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci WARN_ON(host->mrq != NULL); 68062306a36Sopenharmony_ci host->mrq = mrq; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS); 68362306a36Sopenharmony_ci if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) { 68462306a36Sopenharmony_ci host->mrq->cmd->error = -ENOMEDIUM; 68562306a36Sopenharmony_ci tasklet_schedule(&host->finish_tasklet); 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci via_sdc_send_command(host, mrq->cmd); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void via_sdc_set_power(struct via_crdr_mmc_host *host, 69462306a36Sopenharmony_ci unsigned short power, unsigned int on) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci unsigned long flags; 69762306a36Sopenharmony_ci u8 gatt; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci host->power = (1 << power); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci gatt = readb(host->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 70462306a36Sopenharmony_ci if (host->power == MMC_VDD_165_195) 70562306a36Sopenharmony_ci gatt &= ~VIA_CRDR_PCICLKGATT_3V3; 70662306a36Sopenharmony_ci else 70762306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_3V3; 70862306a36Sopenharmony_ci if (on) 70962306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_PAD_PWRON; 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci gatt &= ~VIA_CRDR_PCICLKGATT_PAD_PWRON; 71262306a36Sopenharmony_ci writeb(gatt, host->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci via_pwron_sleep(host); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void via_sdc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct via_crdr_mmc_host *host; 72262306a36Sopenharmony_ci unsigned long flags; 72362306a36Sopenharmony_ci void __iomem *addrbase; 72462306a36Sopenharmony_ci u32 org_data, sdextctrl; 72562306a36Sopenharmony_ci u8 clock; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci host = mmc_priv(mmc); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 73262306a36Sopenharmony_ci org_data = readl(addrbase + VIA_CRDR_SDBUSMODE); 73362306a36Sopenharmony_ci sdextctrl = readl(addrbase + VIA_CRDR_SDEXTCTRL); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (ios->bus_width == MMC_BUS_WIDTH_1) 73662306a36Sopenharmony_ci org_data &= ~VIA_CRDR_SDMODE_4BIT; 73762306a36Sopenharmony_ci else 73862306a36Sopenharmony_ci org_data |= VIA_CRDR_SDMODE_4BIT; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (ios->power_mode == MMC_POWER_OFF) 74162306a36Sopenharmony_ci org_data &= ~VIA_CRDR_SDMODE_CLK_ON; 74262306a36Sopenharmony_ci else 74362306a36Sopenharmony_ci org_data |= VIA_CRDR_SDMODE_CLK_ON; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (ios->timing == MMC_TIMING_SD_HS) 74662306a36Sopenharmony_ci sdextctrl |= VIA_CRDR_SDEXTCTRL_HISPD; 74762306a36Sopenharmony_ci else 74862306a36Sopenharmony_ci sdextctrl &= ~VIA_CRDR_SDEXTCTRL_HISPD; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci writel(org_data, addrbase + VIA_CRDR_SDBUSMODE); 75162306a36Sopenharmony_ci writel(sdextctrl, addrbase + VIA_CRDR_SDEXTCTRL); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (ios->clock >= 48000000) 75462306a36Sopenharmony_ci clock = PCI_CLK_48M; 75562306a36Sopenharmony_ci else if (ios->clock >= 33000000) 75662306a36Sopenharmony_ci clock = PCI_CLK_33M; 75762306a36Sopenharmony_ci else if (ios->clock >= 24000000) 75862306a36Sopenharmony_ci clock = PCI_CLK_24M; 75962306a36Sopenharmony_ci else if (ios->clock >= 16000000) 76062306a36Sopenharmony_ci clock = PCI_CLK_16M; 76162306a36Sopenharmony_ci else if (ios->clock >= 12000000) 76262306a36Sopenharmony_ci clock = PCI_CLK_12M; 76362306a36Sopenharmony_ci else if (ios->clock >= 8000000) 76462306a36Sopenharmony_ci clock = PCI_CLK_8M; 76562306a36Sopenharmony_ci else 76662306a36Sopenharmony_ci clock = PCI_CLK_375K; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 76962306a36Sopenharmony_ci if (readb(addrbase + VIA_CRDR_PCISDCCLK) != clock) 77062306a36Sopenharmony_ci writeb(clock, addrbase + VIA_CRDR_PCISDCCLK); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (ios->power_mode != MMC_POWER_OFF) 77562306a36Sopenharmony_ci via_sdc_set_power(host, ios->vdd, 1); 77662306a36Sopenharmony_ci else 77762306a36Sopenharmony_ci via_sdc_set_power(host, ios->vdd, 0); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int via_sdc_get_ro(struct mmc_host *mmc) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct via_crdr_mmc_host *host; 78362306a36Sopenharmony_ci unsigned long flags; 78462306a36Sopenharmony_ci u16 status; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci host = mmc_priv(mmc); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return !(status & VIA_CRDR_SDSTS_WP); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic const struct mmc_host_ops via_sdc_ops = { 79862306a36Sopenharmony_ci .request = via_sdc_request, 79962306a36Sopenharmony_ci .set_ios = via_sdc_set_ios, 80062306a36Sopenharmony_ci .get_ro = via_sdc_get_ro, 80162306a36Sopenharmony_ci}; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void via_reset_pcictrl(struct via_crdr_mmc_host *host) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci unsigned long flags; 80662306a36Sopenharmony_ci u8 gatt; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci via_save_pcictrlreg(host); 81162306a36Sopenharmony_ci via_save_sdcreg(host); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci gatt = VIA_CRDR_PCICLKGATT_PAD_PWRON; 81662306a36Sopenharmony_ci if (host->power == MMC_VDD_165_195) 81762306a36Sopenharmony_ci gatt &= VIA_CRDR_PCICLKGATT_3V3; 81862306a36Sopenharmony_ci else 81962306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_3V3; 82062306a36Sopenharmony_ci writeb(gatt, host->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 82162306a36Sopenharmony_ci via_pwron_sleep(host); 82262306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_SFTRST; 82362306a36Sopenharmony_ci writeb(gatt, host->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 82462306a36Sopenharmony_ci msleep(3); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci via_restore_pcictrlreg(host); 82962306a36Sopenharmony_ci via_restore_sdcreg(host); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci BUG_ON(intmask == 0); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!host->cmd) { 83962306a36Sopenharmony_ci pr_err("%s: Got command interrupt 0x%x even " 84062306a36Sopenharmony_ci "though no command operation was in progress.\n", 84162306a36Sopenharmony_ci mmc_hostname(host->mmc), intmask); 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (intmask & VIA_CRDR_SDSTS_CRTO) 84662306a36Sopenharmony_ci host->cmd->error = -ETIMEDOUT; 84762306a36Sopenharmony_ci else if (intmask & VIA_CRDR_SDSTS_SC) 84862306a36Sopenharmony_ci host->cmd->error = -EILSEQ; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (host->cmd->error) 85162306a36Sopenharmony_ci tasklet_schedule(&host->finish_tasklet); 85262306a36Sopenharmony_ci else if (intmask & VIA_CRDR_SDSTS_CRD) 85362306a36Sopenharmony_ci via_sdc_finish_command(host); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic void via_sdc_data_isr(struct via_crdr_mmc_host *host, u16 intmask) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci BUG_ON(intmask == 0); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (!host->data) 86162306a36Sopenharmony_ci return; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (intmask & VIA_CRDR_SDSTS_DT) 86462306a36Sopenharmony_ci host->data->error = -ETIMEDOUT; 86562306a36Sopenharmony_ci else if (intmask & (VIA_CRDR_SDSTS_RC | VIA_CRDR_SDSTS_WC)) 86662306a36Sopenharmony_ci host->data->error = -EILSEQ; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci via_sdc_finish_data(host); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic irqreturn_t via_sdc_isr(int irq, void *dev_id) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct via_crdr_mmc_host *sdhost = dev_id; 87462306a36Sopenharmony_ci void __iomem *addrbase; 87562306a36Sopenharmony_ci u8 pci_status; 87662306a36Sopenharmony_ci u16 sd_status; 87762306a36Sopenharmony_ci irqreturn_t result; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!sdhost) 88062306a36Sopenharmony_ci return IRQ_NONE; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci spin_lock(&sdhost->lock); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci addrbase = sdhost->pcictrl_mmiobase; 88562306a36Sopenharmony_ci pci_status = readb(addrbase + VIA_CRDR_PCIINTSTATUS); 88662306a36Sopenharmony_ci if (!(pci_status & VIA_CRDR_PCIINTSTATUS_SDC)) { 88762306a36Sopenharmony_ci result = IRQ_NONE; 88862306a36Sopenharmony_ci goto out; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci addrbase = sdhost->sdhc_mmiobase; 89262306a36Sopenharmony_ci sd_status = readw(addrbase + VIA_CRDR_SDSTATUS); 89362306a36Sopenharmony_ci sd_status &= VIA_CRDR_SDSTS_INT_MASK; 89462306a36Sopenharmony_ci sd_status &= ~VIA_CRDR_SDSTS_IGN_MASK; 89562306a36Sopenharmony_ci if (!sd_status) { 89662306a36Sopenharmony_ci result = IRQ_NONE; 89762306a36Sopenharmony_ci goto out; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (sd_status & VIA_CRDR_SDSTS_CIR) { 90162306a36Sopenharmony_ci writew(sd_status & VIA_CRDR_SDSTS_CIR, 90262306a36Sopenharmony_ci addrbase + VIA_CRDR_SDSTATUS); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci schedule_work(&sdhost->carddet_work); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci sd_status &= ~VIA_CRDR_SDSTS_CIR; 90862306a36Sopenharmony_ci if (sd_status & VIA_CRDR_SDSTS_CMD_MASK) { 90962306a36Sopenharmony_ci writew(sd_status & VIA_CRDR_SDSTS_CMD_MASK, 91062306a36Sopenharmony_ci addrbase + VIA_CRDR_SDSTATUS); 91162306a36Sopenharmony_ci via_sdc_cmd_isr(sdhost, sd_status & VIA_CRDR_SDSTS_CMD_MASK); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci if (sd_status & VIA_CRDR_SDSTS_DATA_MASK) { 91462306a36Sopenharmony_ci writew(sd_status & VIA_CRDR_SDSTS_DATA_MASK, 91562306a36Sopenharmony_ci addrbase + VIA_CRDR_SDSTATUS); 91662306a36Sopenharmony_ci via_sdc_data_isr(sdhost, sd_status & VIA_CRDR_SDSTS_DATA_MASK); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci sd_status &= ~(VIA_CRDR_SDSTS_CMD_MASK | VIA_CRDR_SDSTS_DATA_MASK); 92062306a36Sopenharmony_ci if (sd_status) { 92162306a36Sopenharmony_ci pr_err("%s: Unexpected interrupt 0x%x\n", 92262306a36Sopenharmony_ci mmc_hostname(sdhost->mmc), sd_status); 92362306a36Sopenharmony_ci writew(sd_status, addrbase + VIA_CRDR_SDSTATUS); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci result = IRQ_HANDLED; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ciout: 92962306a36Sopenharmony_ci spin_unlock(&sdhost->lock); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return result; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic void via_sdc_timeout(struct timer_list *t) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct via_crdr_mmc_host *sdhost; 93762306a36Sopenharmony_ci unsigned long flags; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci sdhost = from_timer(sdhost, t, timer); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci spin_lock_irqsave(&sdhost->lock, flags); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (sdhost->mrq) { 94462306a36Sopenharmony_ci pr_err("%s: Timeout waiting for hardware interrupt." 94562306a36Sopenharmony_ci "cmd:0x%x\n", mmc_hostname(sdhost->mmc), 94662306a36Sopenharmony_ci sdhost->mrq->cmd->opcode); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (sdhost->data) { 94962306a36Sopenharmony_ci writel(VIA_CRDR_DMACTRL_SFTRST, 95062306a36Sopenharmony_ci sdhost->ddma_mmiobase + VIA_CRDR_DMACTRL); 95162306a36Sopenharmony_ci sdhost->data->error = -ETIMEDOUT; 95262306a36Sopenharmony_ci via_sdc_finish_data(sdhost); 95362306a36Sopenharmony_ci } else { 95462306a36Sopenharmony_ci if (sdhost->cmd) 95562306a36Sopenharmony_ci sdhost->cmd->error = -ETIMEDOUT; 95662306a36Sopenharmony_ci else 95762306a36Sopenharmony_ci sdhost->mrq->cmd->error = -ETIMEDOUT; 95862306a36Sopenharmony_ci tasklet_schedule(&sdhost->finish_tasklet); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci spin_unlock_irqrestore(&sdhost->lock, flags); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic void via_sdc_tasklet_finish(struct tasklet_struct *t) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet); 96862306a36Sopenharmony_ci unsigned long flags; 96962306a36Sopenharmony_ci struct mmc_request *mrq; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci del_timer(&host->timer); 97462306a36Sopenharmony_ci mrq = host->mrq; 97562306a36Sopenharmony_ci host->mrq = NULL; 97662306a36Sopenharmony_ci host->cmd = NULL; 97762306a36Sopenharmony_ci host->data = NULL; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci mmc_request_done(host->mmc, mrq); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic void via_sdc_card_detect(struct work_struct *work) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct via_crdr_mmc_host *host; 98762306a36Sopenharmony_ci void __iomem *addrbase; 98862306a36Sopenharmony_ci unsigned long flags; 98962306a36Sopenharmony_ci u16 status; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci host = container_of(work, struct via_crdr_mmc_host, carddet_work); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci addrbase = host->ddma_mmiobase; 99462306a36Sopenharmony_ci writel(VIA_CRDR_DMACTRL_SFTRST, addrbase + VIA_CRDR_DMACTRL); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci addrbase = host->pcictrl_mmiobase; 99962306a36Sopenharmony_ci writeb(VIA_CRDR_PCIDMACLK_SDC, addrbase + VIA_CRDR_PCIDMACLK); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 100262306a36Sopenharmony_ci status = readw(addrbase + VIA_CRDR_SDSTATUS); 100362306a36Sopenharmony_ci if (!(status & VIA_CRDR_SDSTS_SLOTG)) { 100462306a36Sopenharmony_ci if (host->mrq) { 100562306a36Sopenharmony_ci pr_err("%s: Card removed during transfer!\n", 100662306a36Sopenharmony_ci mmc_hostname(host->mmc)); 100762306a36Sopenharmony_ci host->mrq->cmd->error = -ENOMEDIUM; 100862306a36Sopenharmony_ci tasklet_schedule(&host->finish_tasklet); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci via_reset_pcictrl(host); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci via_print_pcictrl(host); 102162306a36Sopenharmony_ci via_print_sdchc(host); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci mmc_detect_change(host->mmc, msecs_to_jiffies(500)); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic void via_init_mmc_host(struct via_crdr_mmc_host *host) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 102962306a36Sopenharmony_ci void __iomem *addrbase; 103062306a36Sopenharmony_ci u32 lenreg; 103162306a36Sopenharmony_ci u32 status; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci timer_setup(&host->timer, via_sdc_timeout, 0); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci spin_lock_init(&host->lock); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci mmc->f_min = VIA_CRDR_MIN_CLOCK; 103862306a36Sopenharmony_ci mmc->f_max = VIA_CRDR_MAX_CLOCK; 103962306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 104062306a36Sopenharmony_ci mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED; 104162306a36Sopenharmony_ci mmc->ops = &via_sdc_ops; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /*Hardware cannot do scatter lists*/ 104462306a36Sopenharmony_ci mmc->max_segs = 1; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci mmc->max_blk_size = VIA_CRDR_MAX_BLOCK_LENGTH; 104762306a36Sopenharmony_ci mmc->max_blk_count = VIA_CRDR_MAX_BLOCK_COUNT; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; 105062306a36Sopenharmony_ci mmc->max_req_size = mmc->max_seg_size; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci INIT_WORK(&host->carddet_work, via_sdc_card_detect); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 105762306a36Sopenharmony_ci writel(0x0, addrbase + VIA_CRDR_SDINTMASK); 105862306a36Sopenharmony_ci msleep(1); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci lenreg = VIA_CRDR_SDBLKLEN_GPIDET | VIA_CRDR_SDBLKLEN_INTEN; 106162306a36Sopenharmony_ci writel(lenreg, addrbase + VIA_CRDR_SDBLKLEN); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci status = readw(addrbase + VIA_CRDR_SDSTATUS); 106462306a36Sopenharmony_ci status &= VIA_CRDR_SDSTS_W1C_MASK; 106562306a36Sopenharmony_ci writew(status, addrbase + VIA_CRDR_SDSTATUS); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci status = readw(addrbase + VIA_CRDR_SDSTATUS2); 106862306a36Sopenharmony_ci status |= VIA_CRDR_SDSTS_CFE; 106962306a36Sopenharmony_ci writew(status, addrbase + VIA_CRDR_SDSTATUS2); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci writeb(0x0, addrbase + VIA_CRDR_SDEXTCTRL); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci writel(VIA_CRDR_SDACTIVE_INTMASK, addrbase + VIA_CRDR_SDINTMASK); 107462306a36Sopenharmony_ci msleep(1); 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int via_sd_probe(struct pci_dev *pcidev, 107862306a36Sopenharmony_ci const struct pci_device_id *id) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct mmc_host *mmc; 108162306a36Sopenharmony_ci struct via_crdr_mmc_host *sdhost; 108262306a36Sopenharmony_ci u32 base, len; 108362306a36Sopenharmony_ci u8 gatt; 108462306a36Sopenharmony_ci int ret; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci pr_info(DRV_NAME 108762306a36Sopenharmony_ci ": VIA SDMMC controller found at %s [%04x:%04x] (rev %x)\n", 108862306a36Sopenharmony_ci pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, 108962306a36Sopenharmony_ci (int)pcidev->revision); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci ret = pci_enable_device(pcidev); 109262306a36Sopenharmony_ci if (ret) 109362306a36Sopenharmony_ci return ret; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci ret = pci_request_regions(pcidev, DRV_NAME); 109662306a36Sopenharmony_ci if (ret) 109762306a36Sopenharmony_ci goto disable; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci pci_write_config_byte(pcidev, VIA_CRDR_PCI_WORK_MODE, 0); 110062306a36Sopenharmony_ci pci_write_config_byte(pcidev, VIA_CRDR_PCI_DBG_MODE, 0); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct via_crdr_mmc_host), &pcidev->dev); 110362306a36Sopenharmony_ci if (!mmc) { 110462306a36Sopenharmony_ci ret = -ENOMEM; 110562306a36Sopenharmony_ci goto release; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci sdhost = mmc_priv(mmc); 110962306a36Sopenharmony_ci sdhost->mmc = mmc; 111062306a36Sopenharmony_ci dev_set_drvdata(&pcidev->dev, sdhost); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci len = pci_resource_len(pcidev, 0); 111362306a36Sopenharmony_ci base = pci_resource_start(pcidev, 0); 111462306a36Sopenharmony_ci sdhost->mmiobase = ioremap(base, len); 111562306a36Sopenharmony_ci if (!sdhost->mmiobase) { 111662306a36Sopenharmony_ci ret = -ENOMEM; 111762306a36Sopenharmony_ci goto free_mmc_host; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci sdhost->sdhc_mmiobase = 112162306a36Sopenharmony_ci sdhost->mmiobase + VIA_CRDR_SDC_OFF; 112262306a36Sopenharmony_ci sdhost->ddma_mmiobase = 112362306a36Sopenharmony_ci sdhost->mmiobase + VIA_CRDR_DDMA_OFF; 112462306a36Sopenharmony_ci sdhost->pcictrl_mmiobase = 112562306a36Sopenharmony_ci sdhost->mmiobase + VIA_CRDR_PCICTRL_OFF; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci sdhost->power = MMC_VDD_165_195; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci gatt = VIA_CRDR_PCICLKGATT_3V3 | VIA_CRDR_PCICLKGATT_PAD_PWRON; 113062306a36Sopenharmony_ci writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 113162306a36Sopenharmony_ci via_pwron_sleep(sdhost); 113262306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_SFTRST; 113362306a36Sopenharmony_ci writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 113462306a36Sopenharmony_ci msleep(3); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci via_init_mmc_host(sdhost); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ret = 113962306a36Sopenharmony_ci request_irq(pcidev->irq, via_sdc_isr, IRQF_SHARED, DRV_NAME, 114062306a36Sopenharmony_ci sdhost); 114162306a36Sopenharmony_ci if (ret) 114262306a36Sopenharmony_ci goto unmap; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci writeb(VIA_CRDR_PCIINTCTRL_SDCIRQEN, 114562306a36Sopenharmony_ci sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL); 114662306a36Sopenharmony_ci writeb(VIA_CRDR_PCITMOCTRL_1024MS, 114762306a36Sopenharmony_ci sdhost->pcictrl_mmiobase + VIA_CRDR_PCITMOCTRL); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* device-specific quirks */ 115062306a36Sopenharmony_ci if (pcidev->subsystem_vendor == PCI_VENDOR_ID_LENOVO && 115162306a36Sopenharmony_ci pcidev->subsystem_device == 0x3891) 115262306a36Sopenharmony_ci sdhost->quirks = VIA_CRDR_QUIRK_300MS_PWRDELAY; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ret = mmc_add_host(mmc); 115562306a36Sopenharmony_ci if (ret) 115662306a36Sopenharmony_ci goto unmap; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ciunmap: 116162306a36Sopenharmony_ci iounmap(sdhost->mmiobase); 116262306a36Sopenharmony_cifree_mmc_host: 116362306a36Sopenharmony_ci mmc_free_host(mmc); 116462306a36Sopenharmony_cirelease: 116562306a36Sopenharmony_ci pci_release_regions(pcidev); 116662306a36Sopenharmony_cidisable: 116762306a36Sopenharmony_ci pci_disable_device(pcidev); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci return ret; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic void via_sd_remove(struct pci_dev *pcidev) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct via_crdr_mmc_host *sdhost = pci_get_drvdata(pcidev); 117562306a36Sopenharmony_ci unsigned long flags; 117662306a36Sopenharmony_ci u8 gatt; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci spin_lock_irqsave(&sdhost->lock, flags); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci /* Ensure we don't accept more commands from mmc layer */ 118162306a36Sopenharmony_ci sdhost->reject = 1; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Disable generating further interrupts */ 118462306a36Sopenharmony_ci writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (sdhost->mrq) { 118762306a36Sopenharmony_ci pr_err("%s: Controller removed during " 118862306a36Sopenharmony_ci "transfer\n", mmc_hostname(sdhost->mmc)); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* make sure all DMA is stopped */ 119162306a36Sopenharmony_ci writel(VIA_CRDR_DMACTRL_SFTRST, 119262306a36Sopenharmony_ci sdhost->ddma_mmiobase + VIA_CRDR_DMACTRL); 119362306a36Sopenharmony_ci sdhost->mrq->cmd->error = -ENOMEDIUM; 119462306a36Sopenharmony_ci if (sdhost->mrq->stop) 119562306a36Sopenharmony_ci sdhost->mrq->stop->error = -ENOMEDIUM; 119662306a36Sopenharmony_ci tasklet_schedule(&sdhost->finish_tasklet); 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci spin_unlock_irqrestore(&sdhost->lock, flags); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci mmc_remove_host(sdhost->mmc); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci free_irq(pcidev->irq, sdhost); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci del_timer_sync(&sdhost->timer); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci tasklet_kill(&sdhost->finish_tasklet); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* switch off power */ 120962306a36Sopenharmony_ci gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 121062306a36Sopenharmony_ci gatt &= ~VIA_CRDR_PCICLKGATT_PAD_PWRON; 121162306a36Sopenharmony_ci writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci iounmap(sdhost->mmiobase); 121462306a36Sopenharmony_ci mmc_free_host(sdhost->mmc); 121562306a36Sopenharmony_ci pci_release_regions(pcidev); 121662306a36Sopenharmony_ci pci_disable_device(pcidev); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci pr_info(DRV_NAME 121962306a36Sopenharmony_ci ": VIA SDMMC controller at %s [%04x:%04x] has been removed\n", 122062306a36Sopenharmony_ci pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct sdhcreg *pm_sdhcreg; 122662306a36Sopenharmony_ci void __iomem *addrbase; 122762306a36Sopenharmony_ci u32 lenreg; 122862306a36Sopenharmony_ci u16 status; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci pm_sdhcreg = &(host->pm_sdhc_reg); 123162306a36Sopenharmony_ci addrbase = host->sdhc_mmiobase; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci writel(0x0, addrbase + VIA_CRDR_SDINTMASK); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci lenreg = VIA_CRDR_SDBLKLEN_GPIDET | VIA_CRDR_SDBLKLEN_INTEN; 123662306a36Sopenharmony_ci writel(lenreg, addrbase + VIA_CRDR_SDBLKLEN); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci status = readw(addrbase + VIA_CRDR_SDSTATUS); 123962306a36Sopenharmony_ci status &= VIA_CRDR_SDSTS_W1C_MASK; 124062306a36Sopenharmony_ci writew(status, addrbase + VIA_CRDR_SDSTATUS); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci status = readw(addrbase + VIA_CRDR_SDSTATUS2); 124362306a36Sopenharmony_ci status |= VIA_CRDR_SDSTS_CFE; 124462306a36Sopenharmony_ci writew(status, addrbase + VIA_CRDR_SDSTATUS2); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci writel(pm_sdhcreg->sdcontrol_reg, addrbase + VIA_CRDR_SDCTRL); 124762306a36Sopenharmony_ci writel(pm_sdhcreg->sdcmdarg_reg, addrbase + VIA_CRDR_SDCARG); 124862306a36Sopenharmony_ci writel(pm_sdhcreg->sdintmask_reg, addrbase + VIA_CRDR_SDINTMASK); 124962306a36Sopenharmony_ci writel(pm_sdhcreg->sdrsptmo_reg, addrbase + VIA_CRDR_SDRSPTMO); 125062306a36Sopenharmony_ci writel(pm_sdhcreg->sdclksel_reg, addrbase + VIA_CRDR_SDCLKSEL); 125162306a36Sopenharmony_ci writel(pm_sdhcreg->sdextctrl_reg, addrbase + VIA_CRDR_SDEXTCTRL); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci via_print_pcictrl(host); 125462306a36Sopenharmony_ci via_print_sdchc(host); 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic int __maybe_unused via_sd_suspend(struct device *dev) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct via_crdr_mmc_host *host; 126062306a36Sopenharmony_ci unsigned long flags; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci host = dev_get_drvdata(dev); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 126562306a36Sopenharmony_ci via_save_pcictrlreg(host); 126662306a36Sopenharmony_ci via_save_sdcreg(host); 126762306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci device_wakeup_enable(dev); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int __maybe_unused via_sd_resume(struct device *dev) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct via_crdr_mmc_host *sdhost; 127762306a36Sopenharmony_ci u8 gatt; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci sdhost = dev_get_drvdata(dev); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci gatt = VIA_CRDR_PCICLKGATT_PAD_PWRON; 128262306a36Sopenharmony_ci if (sdhost->power == MMC_VDD_165_195) 128362306a36Sopenharmony_ci gatt &= ~VIA_CRDR_PCICLKGATT_3V3; 128462306a36Sopenharmony_ci else 128562306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_3V3; 128662306a36Sopenharmony_ci writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 128762306a36Sopenharmony_ci via_pwron_sleep(sdhost); 128862306a36Sopenharmony_ci gatt |= VIA_CRDR_PCICLKGATT_SFTRST; 128962306a36Sopenharmony_ci writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); 129062306a36Sopenharmony_ci msleep(3); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci msleep(100); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci via_restore_pcictrlreg(sdhost); 129562306a36Sopenharmony_ci via_init_sdc_pm(sdhost); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci return 0; 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic struct pci_driver via_sd_driver = { 130362306a36Sopenharmony_ci .name = DRV_NAME, 130462306a36Sopenharmony_ci .id_table = via_ids, 130562306a36Sopenharmony_ci .probe = via_sd_probe, 130662306a36Sopenharmony_ci .remove = via_sd_remove, 130762306a36Sopenharmony_ci .driver.pm = &via_sd_pm_ops, 130862306a36Sopenharmony_ci}; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_cimodule_pci_driver(via_sd_driver); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 131362306a36Sopenharmony_ciMODULE_AUTHOR("VIA Technologies Inc."); 131462306a36Sopenharmony_ciMODULE_DESCRIPTION("VIA SD/MMC Card Interface driver"); 1315