162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Toshiba PCI Secure Digital Host Controller Interface driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Ondrej Zary 662306a36Sopenharmony_ci * Copyright (C) 2007 Richard Betts, All Rights Reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on asic3_mmc.c, copyright (c) 2005 SDG Systems, LLC and, 962306a36Sopenharmony_ci * sdhci.c, copyright (C) 2005-2006 Pierre Ossman 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci#include <linux/scatterlist.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/pm.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci#include <linux/mmc/host.h> 2262306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "toshsd.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRIVER_NAME "toshsd" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct pci_device_id pci_ids[] = { 2962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA, 0x0805) }, 3062306a36Sopenharmony_ci { /* end: all zeroes */ }, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_ids); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void toshsd_init(struct toshsd_host *host) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci /* enable clock */ 3862306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 3962306a36Sopenharmony_ci SD_PCICFG_CLKSTOP_ENABLE_ALL); 4062306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_CARDDETECT, 2); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* reset */ 4362306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_SOFTWARERESET); /* assert */ 4462306a36Sopenharmony_ci mdelay(2); 4562306a36Sopenharmony_ci iowrite16(1, host->ioaddr + SD_SOFTWARERESET); /* deassert */ 4662306a36Sopenharmony_ci mdelay(2); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* Clear card registers */ 4962306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); 5062306a36Sopenharmony_ci iowrite32(0, host->ioaddr + SD_CARDSTATUS); 5162306a36Sopenharmony_ci iowrite32(0, host->ioaddr + SD_ERRORSTATUS0); 5262306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_STOPINTERNAL); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* SDIO clock? */ 5562306a36Sopenharmony_ci iowrite16(0x100, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* enable LED */ 5862306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE1, 5962306a36Sopenharmony_ci SD_PCICFG_LED_ENABLE1_START); 6062306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE2, 6162306a36Sopenharmony_ci SD_PCICFG_LED_ENABLE2_START); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* set interrupt masks */ 6462306a36Sopenharmony_ci iowrite32(~(u32)(SD_CARD_RESP_END | SD_CARD_RW_END 6562306a36Sopenharmony_ci | SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0 6662306a36Sopenharmony_ci | SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE 6762306a36Sopenharmony_ci | SD_BUF_CMD_TIMEOUT), 6862306a36Sopenharmony_ci host->ioaddr + SD_INTMASKCARD); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci iowrite16(0x1000, host->ioaddr + SD_TRANSACTIONCTRL); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Set MMC clock / power. 7462306a36Sopenharmony_ci * Note: This controller uses a simple divider scheme therefore it cannot run 7562306a36Sopenharmony_ci * SD/MMC cards at full speed (24/20MHz). HCLK (=33MHz PCI clock?) is too high 7662306a36Sopenharmony_ci * and the next slowest is 16MHz (div=2). 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic void __toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct toshsd_host *host = mmc_priv(mmc); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (ios->clock) { 8362306a36Sopenharmony_ci u16 clk; 8462306a36Sopenharmony_ci int div = 1; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci while (ios->clock < HCLK / div) 8762306a36Sopenharmony_ci div *= 2; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci clk = div >> 2; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (div == 1) { /* disable the divider */ 9262306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 9362306a36Sopenharmony_ci SD_PCICFG_CLKMODE_DIV_DISABLE); 9462306a36Sopenharmony_ci clk |= SD_CARDCLK_DIV_DISABLE; 9562306a36Sopenharmony_ci } else 9662306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci clk |= SD_CARDCLK_ENABLE_CLOCK; 9962306a36Sopenharmony_ci iowrite16(clk, host->ioaddr + SD_CARDCLOCKCTRL); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci mdelay(10); 10262306a36Sopenharmony_ci } else 10362306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci switch (ios->power_mode) { 10662306a36Sopenharmony_ci case MMC_POWER_OFF: 10762306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, 10862306a36Sopenharmony_ci SD_PCICFG_PWR1_OFF); 10962306a36Sopenharmony_ci mdelay(1); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci case MMC_POWER_UP: 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case MMC_POWER_ON: 11462306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, 11562306a36Sopenharmony_ci SD_PCICFG_PWR1_33V); 11662306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_POWER2, 11762306a36Sopenharmony_ci SD_PCICFG_PWR2_AUTO); 11862306a36Sopenharmony_ci mdelay(20); 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci switch (ios->bus_width) { 12362306a36Sopenharmony_ci case MMC_BUS_WIDTH_1: 12462306a36Sopenharmony_ci iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) 12562306a36Sopenharmony_ci | SD_CARDOPT_C2_MODULE_ABSENT 12662306a36Sopenharmony_ci | SD_CARDOPT_DATA_XFR_WIDTH_1, 12762306a36Sopenharmony_ci host->ioaddr + SD_CARDOPTIONSETUP); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 13062306a36Sopenharmony_ci iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14) 13162306a36Sopenharmony_ci | SD_CARDOPT_C2_MODULE_ABSENT 13262306a36Sopenharmony_ci | SD_CARDOPT_DATA_XFR_WIDTH_4, 13362306a36Sopenharmony_ci host->ioaddr + SD_CARDOPTIONSETUP); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void toshsd_set_led(struct toshsd_host *host, unsigned char state) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci iowrite16(state, host->ioaddr + SDIO_BASE + SDIO_LEDCTRL); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void toshsd_finish_request(struct toshsd_host *host) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct mmc_request *mrq = host->mrq; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Write something to end the command */ 14862306a36Sopenharmony_ci host->mrq = NULL; 14962306a36Sopenharmony_ci host->cmd = NULL; 15062306a36Sopenharmony_ci host->data = NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci toshsd_set_led(host, 0); 15362306a36Sopenharmony_ci mmc_request_done(host->mmc, mrq); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic irqreturn_t toshsd_thread_irq(int irq, void *dev_id) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct toshsd_host *host = dev_id; 15962306a36Sopenharmony_ci struct mmc_data *data = host->data; 16062306a36Sopenharmony_ci struct sg_mapping_iter *sg_miter = &host->sg_miter; 16162306a36Sopenharmony_ci unsigned short *buf; 16262306a36Sopenharmony_ci int count; 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!data) { 16662306a36Sopenharmony_ci dev_warn(&host->pdev->dev, "Spurious Data IRQ\n"); 16762306a36Sopenharmony_ci if (host->cmd) { 16862306a36Sopenharmony_ci host->cmd->error = -EIO; 16962306a36Sopenharmony_ci toshsd_finish_request(host); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return IRQ_NONE; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!sg_miter_next(sg_miter)) 17662306a36Sopenharmony_ci goto done; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci buf = sg_miter->addr; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Ensure we dont read more than one block. The chip will interrupt us 18162306a36Sopenharmony_ci * When the next block is available. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci count = sg_miter->length; 18462306a36Sopenharmony_ci if (count > data->blksz) 18562306a36Sopenharmony_ci count = data->blksz; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "count: %08x, flags %08x\n", count, 18862306a36Sopenharmony_ci data->flags); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Transfer the data */ 19162306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) 19262306a36Sopenharmony_ci ioread32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci iowrite32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci sg_miter->consumed = count; 19762306a36Sopenharmony_ci sg_miter_stop(sg_miter); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cidone: 20062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return IRQ_HANDLED; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void toshsd_cmd_irq(struct toshsd_host *host) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct mmc_command *cmd = host->cmd; 20862306a36Sopenharmony_ci u8 *buf; 20962306a36Sopenharmony_ci u16 data; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!host->cmd) { 21262306a36Sopenharmony_ci dev_warn(&host->pdev->dev, "Spurious CMD irq\n"); 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci buf = (u8 *)cmd->resp; 21662306a36Sopenharmony_ci host->cmd = NULL; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) { 21962306a36Sopenharmony_ci /* R2 */ 22062306a36Sopenharmony_ci buf[12] = 0xff; 22162306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE0); 22262306a36Sopenharmony_ci buf[13] = data & 0xff; 22362306a36Sopenharmony_ci buf[14] = data >> 8; 22462306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE1); 22562306a36Sopenharmony_ci buf[15] = data & 0xff; 22662306a36Sopenharmony_ci buf[8] = data >> 8; 22762306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE2); 22862306a36Sopenharmony_ci buf[9] = data & 0xff; 22962306a36Sopenharmony_ci buf[10] = data >> 8; 23062306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE3); 23162306a36Sopenharmony_ci buf[11] = data & 0xff; 23262306a36Sopenharmony_ci buf[4] = data >> 8; 23362306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE4); 23462306a36Sopenharmony_ci buf[5] = data & 0xff; 23562306a36Sopenharmony_ci buf[6] = data >> 8; 23662306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE5); 23762306a36Sopenharmony_ci buf[7] = data & 0xff; 23862306a36Sopenharmony_ci buf[0] = data >> 8; 23962306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE6); 24062306a36Sopenharmony_ci buf[1] = data & 0xff; 24162306a36Sopenharmony_ci buf[2] = data >> 8; 24262306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE7); 24362306a36Sopenharmony_ci buf[3] = data & 0xff; 24462306a36Sopenharmony_ci } else if (cmd->flags & MMC_RSP_PRESENT) { 24562306a36Sopenharmony_ci /* R1, R1B, R3, R6, R7 */ 24662306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE0); 24762306a36Sopenharmony_ci buf[0] = data & 0xff; 24862306a36Sopenharmony_ci buf[1] = data >> 8; 24962306a36Sopenharmony_ci data = ioread16(host->ioaddr + SD_RESPONSE1); 25062306a36Sopenharmony_ci buf[2] = data & 0xff; 25162306a36Sopenharmony_ci buf[3] = data >> 8; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "Command IRQ complete %d %d %x\n", 25562306a36Sopenharmony_ci cmd->opcode, cmd->error, cmd->flags); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* If there is data to handle we will 25862306a36Sopenharmony_ci * finish the request in the mmc_data_end_irq handler.*/ 25962306a36Sopenharmony_ci if (host->data) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci toshsd_finish_request(host); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void toshsd_data_end_irq(struct toshsd_host *host) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct mmc_data *data = host->data; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci host->data = NULL; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!data) { 27262306a36Sopenharmony_ci dev_warn(&host->pdev->dev, "Spurious data end IRQ\n"); 27362306a36Sopenharmony_ci return; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (data->error == 0) 27762306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci data->bytes_xfered = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "Completed data request xfr=%d\n", 28262306a36Sopenharmony_ci data->bytes_xfered); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_STOPINTERNAL); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci toshsd_finish_request(host); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic irqreturn_t toshsd_irq(int irq, void *dev_id) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct toshsd_host *host = dev_id; 29262306a36Sopenharmony_ci u32 int_reg, int_mask, int_status, detail; 29362306a36Sopenharmony_ci int error = 0, ret = IRQ_HANDLED; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci spin_lock(&host->lock); 29662306a36Sopenharmony_ci int_status = ioread32(host->ioaddr + SD_CARDSTATUS); 29762306a36Sopenharmony_ci int_mask = ioread32(host->ioaddr + SD_INTMASKCARD); 29862306a36Sopenharmony_ci int_reg = int_status & ~int_mask & ~IRQ_DONT_CARE_BITS; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "IRQ status:%x mask:%x\n", 30162306a36Sopenharmony_ci int_status, int_mask); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* nothing to do: it's not our IRQ */ 30462306a36Sopenharmony_ci if (!int_reg) { 30562306a36Sopenharmony_ci ret = IRQ_NONE; 30662306a36Sopenharmony_ci goto irq_end; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (int_reg & SD_BUF_CMD_TIMEOUT) { 31062306a36Sopenharmony_ci error = -ETIMEDOUT; 31162306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "Timeout\n"); 31262306a36Sopenharmony_ci } else if (int_reg & SD_BUF_CRC_ERR) { 31362306a36Sopenharmony_ci error = -EILSEQ; 31462306a36Sopenharmony_ci dev_err(&host->pdev->dev, "BadCRC\n"); 31562306a36Sopenharmony_ci } else if (int_reg & (SD_BUF_ILLEGAL_ACCESS 31662306a36Sopenharmony_ci | SD_BUF_CMD_INDEX_ERR 31762306a36Sopenharmony_ci | SD_BUF_STOP_BIT_END_ERR 31862306a36Sopenharmony_ci | SD_BUF_OVERFLOW 31962306a36Sopenharmony_ci | SD_BUF_UNDERFLOW 32062306a36Sopenharmony_ci | SD_BUF_DATA_TIMEOUT)) { 32162306a36Sopenharmony_ci dev_err(&host->pdev->dev, "Buffer status error: { %s%s%s%s%s%s}\n", 32262306a36Sopenharmony_ci int_reg & SD_BUF_ILLEGAL_ACCESS ? "ILLEGAL_ACC " : "", 32362306a36Sopenharmony_ci int_reg & SD_BUF_CMD_INDEX_ERR ? "CMD_INDEX " : "", 32462306a36Sopenharmony_ci int_reg & SD_BUF_STOP_BIT_END_ERR ? "STOPBIT_END " : "", 32562306a36Sopenharmony_ci int_reg & SD_BUF_OVERFLOW ? "OVERFLOW " : "", 32662306a36Sopenharmony_ci int_reg & SD_BUF_UNDERFLOW ? "UNDERFLOW " : "", 32762306a36Sopenharmony_ci int_reg & SD_BUF_DATA_TIMEOUT ? "DATA_TIMEOUT " : ""); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci detail = ioread32(host->ioaddr + SD_ERRORSTATUS0); 33062306a36Sopenharmony_ci dev_err(&host->pdev->dev, "detail error status { %s%s%s%s%s%s%s%s%s%s%s%s%s}\n", 33162306a36Sopenharmony_ci detail & SD_ERR0_RESP_CMD_ERR ? "RESP_CMD " : "", 33262306a36Sopenharmony_ci detail & SD_ERR0_RESP_NON_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", 33362306a36Sopenharmony_ci detail & SD_ERR0_RESP_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "", 33462306a36Sopenharmony_ci detail & SD_ERR0_READ_DATA_END_BIT_ERR ? "READ_DATA_END_BIT " : "", 33562306a36Sopenharmony_ci detail & SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR ? "WRITE_CMD_END_BIT " : "", 33662306a36Sopenharmony_ci detail & SD_ERR0_RESP_NON_CMD12_CRC_ERR ? "RESP_CRC " : "", 33762306a36Sopenharmony_ci detail & SD_ERR0_RESP_CMD12_CRC_ERR ? "RESP_CRC " : "", 33862306a36Sopenharmony_ci detail & SD_ERR0_READ_DATA_CRC_ERR ? "READ_DATA_CRC " : "", 33962306a36Sopenharmony_ci detail & SD_ERR0_WRITE_CMD_CRC_ERR ? "WRITE_CMD_CRC " : "", 34062306a36Sopenharmony_ci detail & SD_ERR1_NO_CMD_RESP ? "NO_CMD_RESP " : "", 34162306a36Sopenharmony_ci detail & SD_ERR1_TIMEOUT_READ_DATA ? "READ_DATA_TIMEOUT " : "", 34262306a36Sopenharmony_ci detail & SD_ERR1_TIMEOUT_CRS_STATUS ? "CRS_STATUS_TIMEOUT " : "", 34362306a36Sopenharmony_ci detail & SD_ERR1_TIMEOUT_CRC_BUSY ? "CRC_BUSY_TIMEOUT " : ""); 34462306a36Sopenharmony_ci error = -EIO; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (error) { 34862306a36Sopenharmony_ci if (host->cmd) 34962306a36Sopenharmony_ci host->cmd->error = error; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (error == -ETIMEDOUT) { 35262306a36Sopenharmony_ci iowrite32(int_status & 35362306a36Sopenharmony_ci ~(SD_BUF_CMD_TIMEOUT | SD_CARD_RESP_END), 35462306a36Sopenharmony_ci host->ioaddr + SD_CARDSTATUS); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci toshsd_init(host); 35762306a36Sopenharmony_ci __toshsd_set_ios(host->mmc, &host->mmc->ios); 35862306a36Sopenharmony_ci goto irq_end; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Card insert/remove. The mmc controlling code is stateless. */ 36362306a36Sopenharmony_ci if (int_reg & (SD_CARD_CARD_INSERTED_0 | SD_CARD_CARD_REMOVED_0)) { 36462306a36Sopenharmony_ci iowrite32(int_status & 36562306a36Sopenharmony_ci ~(SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0), 36662306a36Sopenharmony_ci host->ioaddr + SD_CARDSTATUS); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (int_reg & SD_CARD_CARD_INSERTED_0) 36962306a36Sopenharmony_ci toshsd_init(host); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mmc_detect_change(host->mmc, 1); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Data transfer */ 37562306a36Sopenharmony_ci if (int_reg & (SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE)) { 37662306a36Sopenharmony_ci iowrite32(int_status & 37762306a36Sopenharmony_ci ~(SD_BUF_WRITE_ENABLE | SD_BUF_READ_ENABLE), 37862306a36Sopenharmony_ci host->ioaddr + SD_CARDSTATUS); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 38162306a36Sopenharmony_ci goto irq_end; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Command completion */ 38562306a36Sopenharmony_ci if (int_reg & SD_CARD_RESP_END) { 38662306a36Sopenharmony_ci iowrite32(int_status & ~(SD_CARD_RESP_END), 38762306a36Sopenharmony_ci host->ioaddr + SD_CARDSTATUS); 38862306a36Sopenharmony_ci toshsd_cmd_irq(host); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Data transfer completion */ 39262306a36Sopenharmony_ci if (int_reg & SD_CARD_RW_END) { 39362306a36Sopenharmony_ci iowrite32(int_status & ~(SD_CARD_RW_END), 39462306a36Sopenharmony_ci host->ioaddr + SD_CARDSTATUS); 39562306a36Sopenharmony_ci toshsd_data_end_irq(host); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ciirq_end: 39862306a36Sopenharmony_ci spin_unlock(&host->lock); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void toshsd_start_cmd(struct toshsd_host *host, struct mmc_command *cmd) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct mmc_data *data = host->data; 40562306a36Sopenharmony_ci int c = cmd->opcode; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "Command opcode: %d\n", cmd->opcode); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (cmd->opcode == MMC_STOP_TRANSMISSION) { 41062306a36Sopenharmony_ci iowrite16(SD_STOPINT_ISSUE_CMD12, 41162306a36Sopenharmony_ci host->ioaddr + SD_STOPINTERNAL); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci cmd->resp[0] = cmd->opcode; 41462306a36Sopenharmony_ci cmd->resp[1] = 0; 41562306a36Sopenharmony_ci cmd->resp[2] = 0; 41662306a36Sopenharmony_ci cmd->resp[3] = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci toshsd_finish_request(host); 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 42362306a36Sopenharmony_ci case MMC_RSP_NONE: 42462306a36Sopenharmony_ci c |= SD_CMD_RESP_TYPE_NONE; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci case MMC_RSP_R1: 42862306a36Sopenharmony_ci c |= SD_CMD_RESP_TYPE_EXT_R1; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case MMC_RSP_R1B: 43162306a36Sopenharmony_ci c |= SD_CMD_RESP_TYPE_EXT_R1B; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case MMC_RSP_R2: 43462306a36Sopenharmony_ci c |= SD_CMD_RESP_TYPE_EXT_R2; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci case MMC_RSP_R3: 43762306a36Sopenharmony_ci c |= SD_CMD_RESP_TYPE_EXT_R3; 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci dev_err(&host->pdev->dev, "Unknown response type %d\n", 44262306a36Sopenharmony_ci mmc_resp_type(cmd)); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci host->cmd = cmd; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (cmd->opcode == MMC_APP_CMD) 44962306a36Sopenharmony_ci c |= SD_CMD_TYPE_ACMD; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (cmd->opcode == MMC_GO_IDLE_STATE) 45262306a36Sopenharmony_ci c |= (3 << 8); /* removed from ipaq-asic3.h for some reason */ 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (data) { 45562306a36Sopenharmony_ci c |= SD_CMD_DATA_PRESENT; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (data->blocks > 1) { 45862306a36Sopenharmony_ci iowrite16(SD_STOPINT_AUTO_ISSUE_CMD12, 45962306a36Sopenharmony_ci host->ioaddr + SD_STOPINTERNAL); 46062306a36Sopenharmony_ci c |= SD_CMD_MULTI_BLOCK; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) 46462306a36Sopenharmony_ci c |= SD_CMD_TRANSFER_READ; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* MMC_DATA_WRITE does not require a bit to be set */ 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Send the command */ 47062306a36Sopenharmony_ci iowrite32(cmd->arg, host->ioaddr + SD_ARG0); 47162306a36Sopenharmony_ci iowrite16(c, host->ioaddr + SD_CMD); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic void toshsd_start_data(struct toshsd_host *host, struct mmc_data *data) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci unsigned int flags = SG_MITER_ATOMIC; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dev_dbg(&host->pdev->dev, "setup data transfer: blocksize %08x nr_blocks %d, offset: %08x\n", 47962306a36Sopenharmony_ci data->blksz, data->blocks, data->sg->offset); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci host->data = data; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) 48462306a36Sopenharmony_ci flags |= SG_MITER_TO_SG; 48562306a36Sopenharmony_ci else 48662306a36Sopenharmony_ci flags |= SG_MITER_FROM_SG; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Set transfer length and blocksize */ 49162306a36Sopenharmony_ci iowrite16(data->blocks, host->ioaddr + SD_BLOCKCOUNT); 49262306a36Sopenharmony_ci iowrite16(data->blksz, host->ioaddr + SD_CARDXFERDATALEN); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/* Process requests from the MMC layer */ 49662306a36Sopenharmony_cistatic void toshsd_request(struct mmc_host *mmc, struct mmc_request *mrq) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct toshsd_host *host = mmc_priv(mmc); 49962306a36Sopenharmony_ci unsigned long flags; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* abort if card not present */ 50262306a36Sopenharmony_ci if (!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0)) { 50362306a36Sopenharmony_ci mrq->cmd->error = -ENOMEDIUM; 50462306a36Sopenharmony_ci mmc_request_done(mmc, mrq); 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci WARN_ON(host->mrq != NULL); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci host->mrq = mrq; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (mrq->data) 51562306a36Sopenharmony_ci toshsd_start_data(host, mrq->data); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci toshsd_set_led(host, 1); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci toshsd_start_cmd(host, mrq->cmd); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct toshsd_host *host = mmc_priv(mmc); 52762306a36Sopenharmony_ci unsigned long flags; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 53062306a36Sopenharmony_ci __toshsd_set_ios(mmc, ios); 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int toshsd_get_ro(struct mmc_host *mmc) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct toshsd_host *host = mmc_priv(mmc); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* active low */ 53962306a36Sopenharmony_ci return !(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_WRITE_PROTECT); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int toshsd_get_cd(struct mmc_host *mmc) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct toshsd_host *host = mmc_priv(mmc); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return !!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic const struct mmc_host_ops toshsd_ops = { 55062306a36Sopenharmony_ci .request = toshsd_request, 55162306a36Sopenharmony_ci .set_ios = toshsd_set_ios, 55262306a36Sopenharmony_ci .get_ro = toshsd_get_ro, 55362306a36Sopenharmony_ci .get_cd = toshsd_get_cd, 55462306a36Sopenharmony_ci}; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void toshsd_powerdown(struct toshsd_host *host) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci /* mask all interrupts */ 56062306a36Sopenharmony_ci iowrite32(0xffffffff, host->ioaddr + SD_INTMASKCARD); 56162306a36Sopenharmony_ci /* disable card clock */ 56262306a36Sopenharmony_ci iowrite16(0x000, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL); 56362306a36Sopenharmony_ci iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL); 56462306a36Sopenharmony_ci /* power down card */ 56562306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, SD_PCICFG_PWR1_OFF); 56662306a36Sopenharmony_ci /* disable clock */ 56762306a36Sopenharmony_ci pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 57162306a36Sopenharmony_cistatic int toshsd_pm_suspend(struct device *dev) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 57462306a36Sopenharmony_ci struct toshsd_host *host = pci_get_drvdata(pdev); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci toshsd_powerdown(host); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci pci_save_state(pdev); 57962306a36Sopenharmony_ci pci_enable_wake(pdev, PCI_D3hot, 0); 58062306a36Sopenharmony_ci pci_disable_device(pdev); 58162306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int toshsd_pm_resume(struct device *dev) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 58962306a36Sopenharmony_ci struct toshsd_host *host = pci_get_drvdata(pdev); 59062306a36Sopenharmony_ci int ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 59362306a36Sopenharmony_ci pci_restore_state(pdev); 59462306a36Sopenharmony_ci ret = pci_enable_device(pdev); 59562306a36Sopenharmony_ci if (ret) 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci toshsd_init(host); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int ret; 60762306a36Sopenharmony_ci struct toshsd_host *host; 60862306a36Sopenharmony_ci struct mmc_host *mmc; 60962306a36Sopenharmony_ci resource_size_t base; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = pci_enable_device(pdev); 61262306a36Sopenharmony_ci if (ret) 61362306a36Sopenharmony_ci return ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev); 61662306a36Sopenharmony_ci if (!mmc) { 61762306a36Sopenharmony_ci ret = -ENOMEM; 61862306a36Sopenharmony_ci goto err; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci host = mmc_priv(mmc); 62262306a36Sopenharmony_ci host->mmc = mmc; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci host->pdev = pdev; 62562306a36Sopenharmony_ci pci_set_drvdata(pdev, host); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = pci_request_regions(pdev, DRIVER_NAME); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci goto free; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci host->ioaddr = pci_iomap(pdev, 0, 0); 63262306a36Sopenharmony_ci if (!host->ioaddr) { 63362306a36Sopenharmony_ci ret = -ENOMEM; 63462306a36Sopenharmony_ci goto release; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* Set MMC host parameters */ 63862306a36Sopenharmony_ci mmc->ops = &toshsd_ops; 63962306a36Sopenharmony_ci mmc->caps = MMC_CAP_4_BIT_DATA; 64062306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci mmc->f_min = HCLK / 512; 64362306a36Sopenharmony_ci mmc->f_max = HCLK; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci spin_lock_init(&host->lock); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci toshsd_init(host); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = request_threaded_irq(pdev->irq, toshsd_irq, toshsd_thread_irq, 65062306a36Sopenharmony_ci IRQF_SHARED, DRIVER_NAME, host); 65162306a36Sopenharmony_ci if (ret) 65262306a36Sopenharmony_ci goto unmap; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ret = mmc_add_host(mmc); 65562306a36Sopenharmony_ci if (ret) 65662306a36Sopenharmony_ci goto free_irq; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci base = pci_resource_start(pdev, 0); 65962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci pm_suspend_ignore_children(&pdev->dev, 1); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cifree_irq: 66662306a36Sopenharmony_ci free_irq(pdev->irq, host); 66762306a36Sopenharmony_ciunmap: 66862306a36Sopenharmony_ci pci_iounmap(pdev, host->ioaddr); 66962306a36Sopenharmony_cirelease: 67062306a36Sopenharmony_ci pci_release_regions(pdev); 67162306a36Sopenharmony_cifree: 67262306a36Sopenharmony_ci mmc_free_host(mmc); 67362306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 67462306a36Sopenharmony_cierr: 67562306a36Sopenharmony_ci pci_disable_device(pdev); 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic void toshsd_remove(struct pci_dev *pdev) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct toshsd_host *host = pci_get_drvdata(pdev); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mmc_remove_host(host->mmc); 68462306a36Sopenharmony_ci toshsd_powerdown(host); 68562306a36Sopenharmony_ci free_irq(pdev->irq, host); 68662306a36Sopenharmony_ci pci_iounmap(pdev, host->ioaddr); 68762306a36Sopenharmony_ci pci_release_regions(pdev); 68862306a36Sopenharmony_ci mmc_free_host(host->mmc); 68962306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 69062306a36Sopenharmony_ci pci_disable_device(pdev); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic const struct dev_pm_ops toshsd_pm_ops = { 69462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume) 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic struct pci_driver toshsd_driver = { 69862306a36Sopenharmony_ci .name = DRIVER_NAME, 69962306a36Sopenharmony_ci .id_table = pci_ids, 70062306a36Sopenharmony_ci .probe = toshsd_probe, 70162306a36Sopenharmony_ci .remove = toshsd_remove, 70262306a36Sopenharmony_ci .driver.pm = &toshsd_pm_ops, 70362306a36Sopenharmony_ci}; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cimodule_pci_driver(toshsd_driver); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciMODULE_AUTHOR("Ondrej Zary, Richard Betts"); 70862306a36Sopenharmony_ciMODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver"); 70962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 710