| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Loongson-2K MMC/SDIO controller driver
- *
- * Copyright (C) 2018-2025 Loongson Technology Corporation Limited.
- *
- */
- #include <linux/bitfield.h>
- #include <linux/bitrev.h>
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/dmaengine.h>
- #include <linux/dma-mapping.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/mmc/core.h>
- #include <linux/mmc/host.h>
- #include <linux/mmc/mmc.h>
- #include <linux/mmc/sd.h>
- #include <linux/mmc/sdio.h>
- #include <linux/mmc/slot-gpio.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- #define LOONGSON2_MMC_REG_CTL 0x00 /* Control Register */
- #define LOONGSON2_MMC_REG_PRE 0x04 /* Prescaler Register */
- #define LOONGSON2_MMC_REG_CARG 0x08 /* Command Register */
- #define LOONGSON2_MMC_REG_CCTL 0x0c /* Command Control Register */
- #define LOONGSON2_MMC_REG_CSTS 0x10 /* Command Status Register */
- #define LOONGSON2_MMC_REG_RSP0 0x14 /* Command Response Register 0 */
- #define LOONGSON2_MMC_REG_RSP1 0x18 /* Command Response Register 1 */
- #define LOONGSON2_MMC_REG_RSP2 0x1c /* Command Response Register 2 */
- #define LOONGSON2_MMC_REG_RSP3 0x20 /* Command Response Register 3 */
- #define LOONGSON2_MMC_REG_TIMER 0x24 /* Data Timeout Register */
- #define LOONGSON2_MMC_REG_BSIZE 0x28 /* Block Size Register */
- #define LOONGSON2_MMC_REG_DCTL 0x2c /* Data Control Register */
- #define LOONGSON2_MMC_REG_DCNT 0x30 /* Data Counter Register */
- #define LOONGSON2_MMC_REG_DSTS 0x34 /* Data Status Register */
- #define LOONGSON2_MMC_REG_FSTS 0x38 /* FIFO Status Register */
- #define LOONGSON2_MMC_REG_INT 0x3c /* Interrupt Register */
- #define LOONGSON2_MMC_REG_DATA 0x40 /* Data Register */
- #define LOONGSON2_MMC_REG_IEN 0x64 /* Interrupt Enable Register */
- /* EMMC DLL Mode Registers */
- #define LOONGSON2_MMC_REG_DLLVAL 0xf0 /* DLL Master Lock-value Register */
- #define LOONGSON2_MMC_REG_DLLCTL 0xf4 /* DLL Control Register */
- #define LOONGSON2_MMC_REG_DELAY 0xf8 /* DLL Delayed Parameter Register */
- #define LOONGSON2_MMC_REG_SEL 0xfc /* Bus Mode Selection Register */
- /* Exclusive DMA R/W Registers */
- #define LOONGSON2_MMC_REG_WDMA_LO 0x400
- #define LOONGSON2_MMC_REG_WDMA_HI 0x404
- #define LOONGSON2_MMC_REG_RDMA_LO 0x800
- #define LOONGSON2_MMC_REG_RDMA_HI 0x804
- /* Bitfields of control register */
- #define LOONGSON2_MMC_CTL_ENCLK BIT(0)
- #define LOONGSON2_MMC_CTL_EXTCLK BIT(1)
- #define LOONGSON2_MMC_CTL_RESET BIT(8)
- /* Bitfields of prescaler register */
- #define LOONGSON2_MMC_PRE GENMASK(9, 0)
- #define LOONGSON2_MMC_PRE_EN BIT(31)
- /* Bitfields of command control register */
- #define LOONGSON2_MMC_CCTL_INDEX GENMASK(5, 0)
- #define LOONGSON2_MMC_CCTL_HOST BIT(6)
- #define LOONGSON2_MMC_CCTL_START BIT(8)
- #define LOONGSON2_MMC_CCTL_WAIT_RSP BIT(9)
- #define LOONGSON2_MMC_CCTL_LONG_RSP BIT(10)
- #define LOONGSON2_MMC_CCTL_ABORT BIT(12)
- #define LOONGSON2_MMC_CCTL_CHECK BIT(13)
- #define LOONGSON2_MMC_CCTL_SDIO BIT(14)
- #define LOONGSON2_MMC_CCTL_CMD6 BIT(18)
- /* Bitfields of command status register */
- #define LOONGSON2_MMC_CSTS_INDEX GENMASK(7, 0)
- #define LOONGSON2_MMC_CSTS_ON BIT(8)
- #define LOONGSON2_MMC_CSTS_RSP BIT(9)
- #define LOONGSON2_MMC_CSTS_TIMEOUT BIT(10)
- #define LOONGSON2_MMC_CSTS_END BIT(11)
- #define LOONGSON2_MMC_CSTS_CRC_ERR BIT(12)
- #define LOONGSON2_MMC_CSTS_AUTO_STOP BIT(13)
- #define LOONGSON2_MMC_CSTS_FIN BIT(14)
- /* Bitfields of data timeout register */
- #define LOONGSON2_MMC_DTIMR GENMASK(23, 0)
- /* Bitfields of block size register */
- #define LOONGSON2_MMC_BSIZE GENMASK(11, 0)
- /* Bitfields of data control register */
- #define LOONGSON2_MMC_DCTL_BNUM GENMASK(11, 0)
- #define LOONGSON2_MMC_DCTL_START BIT(14)
- #define LOONGSON2_MMC_DCTL_ENDMA BIT(15)
- #define LOONGSON2_MMC_DCTL_WIDE BIT(16)
- #define LOONGSON2_MMC_DCTL_RWAIT BIT(17)
- #define LOONGSON2_MMC_DCTL_IO_SUSPEND BIT(18)
- #define LOONGSON2_MMC_DCTL_IO_RESUME BIT(19)
- #define LOONGSON2_MMC_DCTL_RW_RESUME BIT(20)
- #define LOONGSON2_MMC_DCTL_8BIT_BUS BIT(26)
- /* Bitfields of sata counter register */
- #define LOONGSON2_MMC_DCNT_BNUM GENMASK(11, 0)
- #define LOONGSON2_MMC_DCNT_BYTE GENMASK(23, 12)
- /* Bitfields of command status register */
- #define LOONGSON2_MMC_DSTS_RXON BIT(0)
- #define LOONGSON2_MMC_DSTS_TXON BIT(1)
- #define LOONGSON2_MMC_DSTS_SBITERR BIT(2)
- #define LOONGSON2_MMC_DSTS_BUSYFIN BIT(3)
- #define LOONGSON2_MMC_DSTS_XFERFIN BIT(4)
- #define LOONGSON2_MMC_DSTS_DTIMEOUT BIT(5)
- #define LOONGSON2_MMC_DSTS_RXCRC BIT(6)
- #define LOONGSON2_MMC_DSTS_TXCRC BIT(7)
- #define LOONGSON2_MMC_DSTS_IRQ BIT(8)
- #define LOONGSON2_MMC_DSTS_START BIT(13)
- #define LOONGSON2_MMC_DSTS_RESUME BIT(15)
- #define LOONGSON2_MMC_DSTS_SUSPEND BIT(16)
- /* Bitfields of FIFO Status Register */
- #define LOONGSON2_MMC_FSTS_TXFULL BIT(11)
- /* Bitfields of interrupt register */
- #define LOONGSON2_MMC_INT_DFIN BIT(0)
- #define LOONGSON2_MMC_INT_DTIMEOUT BIT(1)
- #define LOONGSON2_MMC_INT_RXCRC BIT(2)
- #define LOONGSON2_MMC_INT_TXCRC BIT(3)
- #define LOONGSON2_MMC_INT_PROGERR BIT(4)
- #define LOONGSON2_MMC_INT_SDIOIRQ BIT(5)
- #define LOONGSON2_MMC_INT_CSENT BIT(6)
- #define LOONGSON2_MMC_INT_CTIMEOUT BIT(7)
- #define LOONGSON2_MMC_INT_RESPCRC BIT(8)
- #define LOONGSON2_MMC_INT_BUSYEND BIT(9)
- /* Bitfields of interrupt enable register */
- #define LOONGSON2_MMC_IEN_DFIN BIT(0)
- #define LOONGSON2_MMC_IEN_DTIMEOUT BIT(1)
- #define LOONGSON2_MMC_IEN_RXCRC BIT(2)
- #define LOONGSON2_MMC_IEN_TXCRC BIT(3)
- #define LOONGSON2_MMC_IEN_PROGERR BIT(4)
- #define LOONGSON2_MMC_IEN_SDIOIRQ BIT(5)
- #define LOONGSON2_MMC_IEN_CSENT BIT(6)
- #define LOONGSON2_MMC_IEN_CTIMEOUT BIT(7)
- #define LOONGSON2_MMC_IEN_RESPCRC BIT(8)
- #define LOONGSON2_MMC_IEN_BUSYEND BIT(9)
- #define LOONGSON2_MMC_IEN_ALL GENMASK(9, 0)
- #define LOONGSON2_MMC_INT_CLEAR GENMASK(9, 0)
- /* Bitfields of DLL master lock-value register */
- #define LOONGSON2_MMC_DLLVAL_DONE BIT(8)
- /* Bitfields of DLL control register */
- #define LOONGSON2_MMC_DLLCTL_TIME GENMASK(7, 0)
- #define LOONGSON2_MMC_DLLCTL_INCRE GENMASK(15, 8)
- #define LOONGSON2_MMC_DLLCTL_START GENMASK(23, 16)
- #define LOONGSON2_MMC_DLLCTL_CLK_MODE BIT(24)
- #define LOONGSON2_MMC_DLLCTL_START_BIT BIT(25)
- #define LOONGSON2_MMC_DLLCTL_TIME_BPASS GENMASK(29, 26)
- #define LOONGSON2_MMC_DELAY_PAD GENMASK(7, 0)
- #define LOONGSON2_MMC_DELAY_RD GENMASK(15, 8)
- #define LOONGSON2_MMC_SEL_DATA BIT(0) /* 0: SDR, 1: DDR */
- #define LOONGSON2_MMC_SEL_BUS BIT(0) /* 0: EMMC, 1: SDIO */
- /* Internal dma controller registers */
- /* Bitfields of Global Configuration Register */
- #define LOONGSON2_MMC_DMA_64BIT_EN BIT(0) /* 1: 64 bit support */
- #define LOONGSON2_MMC_DMA_UNCOHERENT_EN BIT(1) /* 0: cache, 1: uncache */
- #define LOONGSON2_MMC_DMA_ASK_VALID BIT(2)
- #define LOONGSON2_MMC_DMA_START BIT(3) /* DMA start operation */
- #define LOONGSON2_MMC_DMA_STOP BIT(4) /* DMA stop operation */
- #define LOONGSON2_MMC_DMA_CONFIG_MASK GENMASK_ULL(4, 0) /* DMA controller config bits mask */
- /* Bitfields of ndesc_addr field of HW descriptor */
- #define LOONGSON2_MMC_DMA_DESC_EN BIT(0) /*1: The next descriptor is valid */
- #define LOONGSON2_MMC_DMA_DESC_ADDR_LOW GENMASK(31, 1)
- /* Bitfields of cmd field of HW descriptor */
- #define LOONGSON2_MMC_DMA_INT BIT(1) /* Enable DMA interrupts */
- #define LOONGSON2_MMC_DMA_DATA_DIR BIT(12) /* 1: write to device, 0: read from device */
- #define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000
- #define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500
- /* Loongson-2K1000 SDIO2 DMA routing register */
- #define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15)
- #define LS2K1000_DMA0_CONF 0x0
- #define LS2K1000_DMA1_CONF 0x1
- #define LS2K1000_DMA2_CONF 0x2
- #define LS2K1000_DMA3_CONF 0x3
- #define LS2K1000_DMA4_CONF 0x4
- /* Loongson-2K0500 SDIO2 DMA routing register */
- #define LS2K0500_SDIO_DMA_MASK GENMASK(15, 14)
- #define LS2K0500_DMA0_CONF 0x1
- #define LS2K0500_DMA1_CONF 0x2
- #define LS2K0500_DMA2_CONF 0x3
- enum loongson2_mmc_state {
- STATE_NONE,
- STATE_FINALIZE,
- STATE_CMDSENT,
- STATE_RSPFIN,
- STATE_XFERFINISH,
- STATE_XFERFINISH_RSPFIN,
- };
- struct loongson2_dma_desc {
- u32 ndesc_addr;
- u32 mem_addr;
- u32 apb_addr;
- u32 len;
- u32 step_len;
- u32 step_times;
- u32 cmd;
- u32 stats;
- u32 high_ndesc_addr;
- u32 high_mem_addr;
- u32 reserved[2];
- } __packed;
- struct loongson2_mmc_host {
- struct device *dev;
- struct mmc_request *mrq;
- struct regmap *regmap;
- struct resource *res;
- struct clk *clk;
- u32 current_clk;
- void *sg_cpu;
- dma_addr_t sg_dma;
- int dma_complete;
- struct dma_chan *chan;
- int cmd_is_stop;
- int bus_width;
- spinlock_t lock; /* Prevent races with irq handler */
- enum loongson2_mmc_state state;
- const struct loongson2_mmc_pdata *pdata;
- };
- struct loongson2_mmc_pdata {
- const struct regmap_config *regmap_config;
- void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
- void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
- int (*setting_dma)(struct loongson2_mmc_host *host, struct platform_device *pdev);
- int (*prepare_dma)(struct loongson2_mmc_host *host, struct mmc_data *data);
- void (*release_dma)(struct loongson2_mmc_host *host, struct device *dev);
- };
- static void loongson2_mmc_send_command(struct loongson2_mmc_host *host,
- struct mmc_command *cmd)
- {
- u32 cctrl;
- if (cmd->data)
- host->state = STATE_XFERFINISH_RSPFIN;
- else if (cmd->flags & MMC_RSP_PRESENT)
- host->state = STATE_RSPFIN;
- else
- host->state = STATE_CMDSENT;
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg);
- cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode);
- cctrl |= LOONGSON2_MMC_CCTL_HOST | LOONGSON2_MMC_CCTL_START;
- if (cmd->opcode == SD_SWITCH && cmd->data)
- cctrl |= LOONGSON2_MMC_CCTL_CMD6;
- if (cmd->flags & MMC_RSP_PRESENT)
- cctrl |= LOONGSON2_MMC_CCTL_WAIT_RSP;
- if (cmd->flags & MMC_RSP_136)
- cctrl |= LOONGSON2_MMC_CCTL_LONG_RSP;
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl);
- }
- static int loongson2_mmc_setup_data(struct loongson2_mmc_host *host,
- struct mmc_data *data)
- {
- u32 dctrl;
- if ((data->blksz & 3) != 0)
- return -EINVAL;
- dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks);
- dctrl |= LOONGSON2_MMC_DCTL_START | LOONGSON2_MMC_DCTL_ENDMA;
- if (host->bus_width == MMC_BUS_WIDTH_4)
- dctrl |= LOONGSON2_MMC_DCTL_WIDE;
- else if (host->bus_width == MMC_BUS_WIDTH_8)
- dctrl |= LOONGSON2_MMC_DCTL_8BIT_BUS;
- regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX);
- return 0;
- }
- static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host,
- struct mmc_data *data)
- {
- int ret;
- if (!data)
- return 0;
- ret = loongson2_mmc_setup_data(host, data);
- if (ret)
- return ret;
- host->dma_complete = 0;
- return host->pdata->prepare_dma(host, data);
- }
- static void loongson2_mmc_send_request(struct mmc_host *mmc)
- {
- int ret;
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
- ret = loongson2_mmc_prepare_dma(host, cmd->data);
- if (ret) {
- dev_err(host->dev, "DMA data prepared failed with %d\n", ret);
- cmd->error = ret;
- cmd->data->error = ret;
- mmc_request_done(mmc, mrq);
- return;
- }
- if (host->pdata->fix_data_timeout)
- host->pdata->fix_data_timeout(host, cmd);
- loongson2_mmc_send_command(host, cmd);
- /* Fix deselect card */
- if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) {
- cmd->error = 0;
- mmc_request_done(mmc, mrq);
- }
- }
- static irqreturn_t loongson2_mmc_irq_worker(int irq, void *devid)
- {
- struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid;
- struct mmc_host *mmc = mmc_from_priv(host);
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
- if (cmd->data)
- dma_unmap_sg(mmc_dev(mmc), cmd->data->sg, cmd->data->sg_len,
- mmc_get_dma_dir(cmd->data));
- if (cmd->data && !cmd->error &&
- !cmd->data->error && !host->dma_complete)
- return IRQ_HANDLED;
- /* Read response from controller. */
- regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]);
- regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]);
- regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]);
- regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]);
- /* Cleanup controller */
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0);
- if (cmd->data && cmd->error)
- cmd->data->error = cmd->error;
- if (cmd->data && cmd->data->stop && !host->cmd_is_stop) {
- host->cmd_is_stop = 1;
- loongson2_mmc_send_request(mmc);
- return IRQ_HANDLED;
- }
- /* If we have no data transfer we are finished here */
- if (!mrq->data)
- goto request_done;
- /* Calculate the amount of bytes transfer if there was no error */
- if (mrq->data->error == 0) {
- mrq->data->bytes_xfered =
- (mrq->data->blocks * mrq->data->blksz);
- } else {
- mrq->data->bytes_xfered = 0;
- }
- request_done:
- host->state = STATE_NONE;
- host->mrq = NULL;
- mmc_request_done(mmc, mrq);
- return IRQ_HANDLED;
- }
- static irqreturn_t loongson2_mmc_irq(int irq, void *devid)
- {
- struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid;
- struct mmc_host *mmc = mmc_from_priv(host);
- struct mmc_command *cmd;
- unsigned long iflags;
- u32 dsts, imsk;
- regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk);
- regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts);
- if ((dsts & LOONGSON2_MMC_DSTS_IRQ) &&
- (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) {
- regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT,
- LOONGSON2_MMC_INT_SDIOIRQ, LOONGSON2_MMC_INT_SDIOIRQ);
- sdio_signal_irq(mmc);
- return IRQ_HANDLED;
- }
- spin_lock_irqsave(&host->lock, iflags);
- if (host->state == STATE_NONE || host->state == STATE_FINALIZE || !host->mrq)
- goto irq_out;
- cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
- if (!cmd)
- goto irq_out;
- cmd->error = 0;
- if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) {
- cmd->error = -ETIMEDOUT;
- goto close_transfer;
- }
- if (imsk & LOONGSON2_MMC_INT_CSENT) {
- if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT)
- goto close_transfer;
- if (host->state == STATE_XFERFINISH_RSPFIN)
- host->state = STATE_XFERFINISH;
- }
- if (!cmd->data)
- goto irq_out;
- if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) {
- cmd->data->error = -EILSEQ;
- goto close_transfer;
- }
- if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) {
- cmd->data->error = -ETIMEDOUT;
- goto close_transfer;
- }
- if (imsk & LOONGSON2_MMC_INT_DFIN) {
- if (host->state == STATE_XFERFINISH) {
- host->dma_complete = 1;
- goto close_transfer;
- }
- if (host->state == STATE_XFERFINISH_RSPFIN)
- host->state = STATE_RSPFIN;
- }
- irq_out:
- regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
- spin_unlock_irqrestore(&host->lock, iflags);
- return IRQ_HANDLED;
- close_transfer:
- host->state = STATE_FINALIZE;
- host->pdata->reorder_cmd_data(host, cmd);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
- spin_unlock_irqrestore(&host->lock, iflags);
- return IRQ_WAKE_THREAD;
- }
- static void loongson2_mmc_dll_mode_init(struct loongson2_mmc_host *host)
- {
- u32 val, pad_delay, delay;
- int ret;
- regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL,
- LOONGSON2_MMC_SEL_DATA, LOONGSON2_MMC_SEL_DATA);
- val = FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME, 0xc8)
- | FIELD_PREP(LOONGSON2_MMC_DLLCTL_INCRE, 0x1)
- | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START, 0x1)
- | FIELD_PREP(LOONGSON2_MMC_DLLCTL_CLK_MODE, 0x1)
- | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START_BIT, 0x1)
- | FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME_BPASS, 0xf);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val);
- ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val,
- (val & LOONGSON2_MMC_DLLVAL_DONE), 0,
- LOONGSON2_MMC_DLLVAL_TIMEOUT_US);
- if (ret < 0)
- return;
- regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val);
- pad_delay = FIELD_GET(GENMASK(7, 1), val);
- delay = FIELD_PREP(LOONGSON2_MMC_DELAY_PAD, pad_delay)
- | FIELD_PREP(LOONGSON2_MMC_DELAY_RD, pad_delay + 1);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay);
- }
- static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios)
- {
- u32 pre;
- pre = DIV_ROUND_UP(host->current_clk, ios->clock);
- if (pre > 255)
- pre = 255;
- regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN);
- regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
- LOONGSON2_MMC_CTL_ENCLK, LOONGSON2_MMC_CTL_ENCLK);
- /* EMMC DLL mode setting */
- if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52)
- loongson2_mmc_dll_mode_init(host);
- }
- static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
- {
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- int ret;
- if (ios->power_mode == MMC_POWER_UP) {
- if (!IS_ERR(mmc->supply.vmmc)) {
- ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
- if (ret) {
- dev_err(host->dev, "failed to enable vmmc regulator\n");
- return; /* return, if failed turn on vmmc */
- }
- }
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET);
- mdelay(10);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_EXTCLK);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL);
- regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR);
- } else if (ios->power_mode == MMC_POWER_OFF) {
- regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
- LOONGSON2_MMC_CTL_RESET, LOONGSON2_MMC_CTL_RESET);
- if (!IS_ERR(mmc->supply.vmmc))
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
- return;
- }
- loongson2_mmc_set_clk(host, ios);
- host->bus_width = ios->bus_width;
- }
- static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
- {
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- host->cmd_is_stop = 0;
- host->mrq = mrq;
- loongson2_mmc_send_request(mmc);
- }
- static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
- {
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_SDIOIRQ, enable);
- }
- static void loongson2_mmc_ack_sdio_irq(struct mmc_host *mmc)
- {
- loongson2_mmc_enable_sdio_irq(mmc, 1);
- }
- static struct mmc_host_ops loongson2_mmc_ops = {
- .request = loongson2_mmc_request,
- .set_ios = loongson2_mmc_set_ios,
- .get_ro = mmc_gpio_get_ro,
- .get_cd = mmc_gpio_get_cd,
- .enable_sdio_irq = loongson2_mmc_enable_sdio_irq,
- .ack_sdio_irq = loongson2_mmc_ack_sdio_irq,
- };
- static const struct regmap_config ls2k0500_mmc_regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = LOONGSON2_MMC_REG_IEN,
- };
- static int loongson2_reorder_cmd_list[] = { SD_APP_SEND_SCR, SD_APP_SEND_NUM_WR_BLKS,
- SD_APP_SD_STATUS, MMC_SEND_WRITE_PROT, SD_SWITCH };
- /*
- * According to SD spec, ACMD13, ACMD22, ACMD51 and CMD30
- * response datas has different byte order with usual data packets.
- * However sdio controller will send these datas in usual data format,
- * so we need to adjust these datas to a protocol consistent byte order.
- */
- static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
- struct mmc_command *cmd)
- {
- struct scatterlist *sg;
- u32 *data;
- int i, j;
- if (mmc_cmd_type(cmd) != MMC_CMD_ADTC)
- return;
- for (i = 0; i < ARRAY_SIZE(loongson2_reorder_cmd_list); i++)
- if (cmd->opcode == loongson2_reorder_cmd_list[i])
- break;
- if (i == ARRAY_SIZE(loongson2_reorder_cmd_list))
- return;
- for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
- data = sg_virt(&sg[i]);
- for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
- if (cmd->opcode == SD_SWITCH)
- data[j] = bitrev8x4(data[j]);
- else
- data[j] = (__force u32)cpu_to_be32(data[j]);
- }
- }
- static int loongson2_mmc_prepare_external_dma(struct loongson2_mmc_host *host,
- struct mmc_data *data)
- {
- struct mmc_host *mmc = mmc_from_priv(host);
- struct dma_slave_config dma_conf = { };
- struct dma_async_tx_descriptor *desc;
- int ret;
- ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
- mmc_get_dma_dir(data));
- if (!ret)
- return -ENOMEM;
- dma_conf.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
- dma_conf.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
- dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- dma_conf.direction = !(data->flags & MMC_DATA_WRITE) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
- dmaengine_slave_config(host->chan, &dma_conf);
- desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len,
- dma_conf.direction,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
- if (!desc)
- goto unmap_exit;
- dmaengine_submit(desc);
- dma_async_issue_pending(host->chan);
- return 0;
- unmap_exit:
- dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, mmc_get_dma_dir(data));
- return -ENOMEM;
- }
- static void loongson2_mmc_release_external_dma(struct loongson2_mmc_host *host,
- struct device *dev)
- {
- dma_release_channel(host->chan);
- }
- static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host,
- struct platform_device *pdev)
- {
- int ret, val;
- void __iomem *regs;
- regs = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
- val = readl(regs);
- val |= FIELD_PREP(LS2K0500_SDIO_DMA_MASK, LS2K0500_DMA2_CONF);
- writel(val, regs);
- host->chan = dma_request_chan(&pdev->dev, "rx-tx");
- ret = PTR_ERR_OR_ZERO(host->chan);
- if (ret) {
- dev_err(&pdev->dev, "Cannot get DMA channel.\n");
- return ret;
- }
- return 0;
- }
- static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
- .regmap_config = &ls2k0500_mmc_regmap_config,
- .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
- .setting_dma = ls2k0500_mmc_set_external_dma,
- .prepare_dma = loongson2_mmc_prepare_external_dma,
- .release_dma = loongson2_mmc_release_external_dma,
- };
- static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
- struct platform_device *pdev)
- {
- int ret, val;
- void __iomem *regs;
- regs = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
- val = readl(regs);
- val |= FIELD_PREP(LS2K1000_SDIO_DMA_MASK, LS2K1000_DMA1_CONF);
- writel(val, regs);
- host->chan = dma_request_chan(&pdev->dev, "rx-tx");
- ret = PTR_ERR_OR_ZERO(host->chan);
- if (ret) {
- dev_err(&pdev->dev, "Cannot get DMA channel.\n");
- return ret;
- }
- return 0;
- }
- static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
- .regmap_config = &ls2k0500_mmc_regmap_config,
- .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
- .setting_dma = ls2k1000_mmc_set_external_dma,
- .prepare_dma = loongson2_mmc_prepare_external_dma,
- .release_dma = loongson2_mmc_release_external_dma,
- };
- static const struct regmap_config ls2k2000_mmc_regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = LOONGSON2_MMC_REG_RDMA_HI,
- };
- static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
- struct mmc_command *cmd)
- {
- struct scatterlist *sg;
- u32 *data;
- int i, j;
- if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC)
- return;
- for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
- data = sg_virt(&sg[i]);
- for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
- data[j] = bitrev8x4(data[j]);
- }
- }
- /*
- * This is a controller hardware defect. Single/multiple block write commands
- * must be sent after the TX FULL flag is set, otherwise a data timeout interrupt
- * will occur.
- */
- static void ls2k2000_mmc_fix_data_timeout(struct loongson2_mmc_host *host,
- struct mmc_command *cmd)
- {
- int val;
- if (cmd->opcode != MMC_WRITE_BLOCK && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK)
- return;
- regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val,
- (val & LOONGSON2_MMC_FSTS_TXFULL), 0,
- LOONGSON2_MMC_TXFULL_TIMEOUT_US);
- }
- static int loongson2_mmc_prepare_internal_dma(struct loongson2_mmc_host *host,
- struct mmc_data *data)
- {
- struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu;
- struct mmc_host *mmc = mmc_from_priv(host);
- dma_addr_t next_desc = host->sg_dma;
- struct scatterlist *sg;
- int reg_lo, reg_hi;
- u64 dma_order;
- int i, ret;
- ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
- mmc_get_dma_dir(data));
- if (!ret)
- return -ENOMEM;
- for_each_sg(data->sg, sg, data->sg_len, i) {
- pdes[i].len = sg_dma_len(&sg[i]) / 4;
- pdes[i].step_len = 0;
- pdes[i].step_times = 1;
- pdes[i].mem_addr = lower_32_bits(sg_dma_address(&sg[i]));
- pdes[i].high_mem_addr = upper_32_bits(sg_dma_address(&sg[i]));
- pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA;
- pdes[i].cmd = LOONGSON2_MMC_DMA_INT;
- if (data->flags & MMC_DATA_READ) {
- reg_lo = LOONGSON2_MMC_REG_RDMA_LO;
- reg_hi = LOONGSON2_MMC_REG_RDMA_HI;
- } else {
- pdes[i].cmd |= LOONGSON2_MMC_DMA_DATA_DIR;
- reg_lo = LOONGSON2_MMC_REG_WDMA_LO;
- reg_hi = LOONGSON2_MMC_REG_WDMA_HI;
- }
- next_desc += sizeof(struct loongson2_dma_desc);
- pdes[i].ndesc_addr = lower_32_bits(next_desc) |
- LOONGSON2_MMC_DMA_DESC_EN;
- pdes[i].high_ndesc_addr = upper_32_bits(next_desc);
- }
- /* Setting the last descriptor enable bit */
- pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN;
- dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) |
- LOONGSON2_MMC_DMA_64BIT_EN |
- LOONGSON2_MMC_DMA_START;
- regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order));
- regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order));
- return 0;
- }
- static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host,
- struct platform_device *pdev)
- {
- host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
- &host->sg_dma, GFP_KERNEL);
- if (!host->sg_cpu)
- return -ENOMEM;
- memset(host->sg_cpu, 0, PAGE_SIZE);
- return 0;
- }
- static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host,
- struct device *dev)
- {
- dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
- }
- static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = {
- .regmap_config = &ls2k2000_mmc_regmap_config,
- .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
- .fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
- .setting_dma = ls2k2000_mmc_set_internal_dma,
- .prepare_dma = loongson2_mmc_prepare_internal_dma,
- .release_dma = loongson2_mmc_release_internal_dma,
- };
- static int loongson2_mmc_resource_request(struct platform_device *pdev,
- struct loongson2_mmc_host *host)
- {
- struct device *dev = &pdev->dev;
- void __iomem *base;
- int ret, irq;
- base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res);
- if (IS_ERR(base))
- return PTR_ERR(base);
- host->regmap = devm_regmap_init_mmio(dev, base, host->pdata->regmap_config);
- if (IS_ERR(host->regmap))
- return PTR_ERR(host->regmap);
- host->clk = devm_clk_get_optional_enabled(dev, NULL);
- if (IS_ERR(host->clk))
- return PTR_ERR(host->clk);
- if (host->clk) {
- ret = devm_clk_rate_exclusive_get(dev, host->clk);
- if (ret)
- return ret;
- host->current_clk = clk_get_rate(host->clk);
- } else {
- /* For ACPI, the clock is accessed via the clock-frequency attribute. */
- device_property_read_u32(dev, "clock-frequency", &host->current_clk);
- }
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
- ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq,
- loongson2_mmc_irq_worker,
- IRQF_ONESHOT, "loongson2-mmc", host);
- if (ret)
- return ret;
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
- if (ret)
- return ret;
- return host->pdata->setting_dma(host, pdev);
- }
- static int loongson2_mmc_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct loongson2_mmc_host *host;
- struct mmc_host *mmc;
- int ret;
- mmc = devm_mmc_alloc_host(dev, sizeof(*host));
- if (!mmc)
- return -ENOMEM;
- platform_set_drvdata(pdev, mmc);
- host = mmc_priv(mmc);
- host->state = STATE_NONE;
- spin_lock_init(&host->lock);
- host->pdata = device_get_match_data(dev);
- if (!host->pdata)
- return dev_err_probe(dev, -EINVAL, "Failed to get match data\n");
- ret = loongson2_mmc_resource_request(pdev, host);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to request resource\n");
- mmc->ops = &loongson2_mmc_ops;
- mmc->f_min = DIV_ROUND_UP(host->current_clk, 256);
- mmc->f_max = host->current_clk;
- mmc->max_blk_count = 4095;
- mmc->max_blk_size = 4095;
- mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
- mmc->max_segs = 1;
- mmc->max_seg_size = mmc->max_req_size;
- /* Process SDIO IRQs through the sdio_irq_work. */
- if (mmc->caps & MMC_CAP_SDIO_IRQ)
- mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
- ret = mmc_regulator_get_supply(mmc);
- if (ret || mmc->ocr_avail == 0) {
- dev_warn(dev, "Can't get voltage, defaulting to 3.3V\n");
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- }
- ret = mmc_of_parse(mmc);
- if (ret) {
- dev_err(dev, "Failed to parse device node\n");
- goto free_dma;
- }
- ret = mmc_add_host(mmc);
- if (ret) {
- dev_err(dev, "Failed to add mmc host\n");
- goto free_dma;
- }
- return 0;
- free_dma:
- host->pdata->release_dma(host, dev);
- return ret;
- }
- static void loongson2_mmc_remove(struct platform_device *pdev)
- {
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- mmc_remove_host(mmc);
- host->pdata->release_dma(host, &pdev->dev);
- }
- static const struct of_device_id loongson2_mmc_of_ids[] = {
- { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
- { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
- { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
- { },
- };
- MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids);
- static int loongson2_mmc_suspend(struct device *dev)
- {
- struct mmc_host *mmc = dev_get_drvdata(dev);
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- clk_disable_unprepare(host->clk);
- return 0;
- }
- static int loongson2_mmc_resume(struct device *dev)
- {
- struct mmc_host *mmc = dev_get_drvdata(dev);
- struct loongson2_mmc_host *host = mmc_priv(mmc);
- return clk_prepare_enable(host->clk);
- }
- static DEFINE_SIMPLE_DEV_PM_OPS(loongson2_mmc_pm_ops, loongson2_mmc_suspend, loongson2_mmc_resume);
- static struct platform_driver loongson2_mmc_driver = {
- .driver = {
- .name = "loongson2-mmc",
- .of_match_table = loongson2_mmc_of_ids,
- .pm = pm_ptr(&loongson2_mmc_pm_ops),
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- .probe = loongson2_mmc_probe,
- .remove = loongson2_mmc_remove,
- };
- module_platform_driver(loongson2_mmc_driver);
- MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver");
- MODULE_AUTHOR("Loongson Technology Corporation Limited");
- MODULE_LICENSE("GPL");
|