mmc: support for CNC1800L sdio (experimental)

This commit is contained in:
haru 2023-07-18 17:55:49 +09:00
parent c296381a1d
commit a09682c8f8
6 changed files with 2268 additions and 0 deletions

View File

@ -115,4 +115,11 @@
// interrupts = <6>;
cavium,nrdevs = <14>;
};
sdio: cncsdio@80290000{
compatible = "cavium,cnc1800l-sdio";
reg = <0x80290000 0x1000>;
clocks = <&pclk>;
interrupts = <18>;
};
};

View File

@ -55,6 +55,27 @@ config MMC_PXA
If unsure, say N.
config MMC_MSHCI
tristate "Mobile Storage Host Controller Interface support"
depends on HAS_DMA
help
This selects the Mobile Storage Host Controller Interface.
If support SD/MMC card.
If you have a controller with this interface, say Y or M here. You
also need to enable an appropriate bus interface.
If unsure, say N.
config MMC_CELESTIAL
tristate "Cavium Celestial CNC1800L SDIO"
depends on HAS_DMA && MMC_MSHCI
help
This selects Cavium CNC1800L SDIO Controller.
If support SDIO interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI
tristate "Secure Digital Host Controller Interface support"
depends on HAS_DMA

View File

@ -10,6 +10,8 @@ armmmci-$(CONFIG_MMC_STM32_SDMMC) += mmci_stm32_sdmmc.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_MSHCI) += mshci.o
obj-$(CONFIG_MMC_CELESTIAL) += cnc1800l-sdio.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \

View File

@ -0,0 +1,200 @@
/*
* Mobile Storage Host Controller Interface driver
* Scott Shu <sshu@caviumnetworks.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
// based on mvsdio.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mbus.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/of_irq.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/sizes.h>
#include <asm/unaligned.h>
#include "mshci.h"
#define DRIVER_NAME "cnc1800l-sdio"
struct cnc1800lsd_host {
struct platform_device *pdev;
struct mshci_host *host;
void __iomem *base;
unsigned long __iomem *ctrl1;
unsigned long __iomem *ctrl2;
struct mmc_request *mrq;
spinlock_t lock;
struct mmc_host *mmc;
struct device *dev;
struct clk *clk;
};
static inline struct cnc1800lsd_host *to_cnc1800l(struct mshci_host *host)
{
return mshci_priv(host);
}
static unsigned int mshci_cnc1800l_get_max_clk(struct mshci_host *host)
{
int clk = 47250000; /* 47.25M */
return clk;
}
static void mshci_cnc1800l_set_clock(struct mshci_host *host, unsigned int clock)
{
/* don't bother if the clock is going off. */
if (clock == 0)
return;
host->max_clk = 47250000;
}
static void mshci_cnc1800l_set_ios(struct mshci_host *host, struct mmc_ios *ios)
{
/* nothing to do */
}
static struct mshci_ops mshci_cnc1800l_ops = {
.get_max_clock = mshci_cnc1800l_get_max_clk,
.set_clock = mshci_cnc1800l_set_clock,
.set_ios = mshci_cnc1800l_set_ios,
// .init_card = mshci_cnc1800l_init_card,
};
static int cnc1800lsd_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mshci_host *host = NULL;
struct device *dev = &pdev->dev;
struct cnc1800lsd_host *cardhost = NULL;
int ret, irq;
if (!np) {
dev_err(&pdev->dev, "no DT node\n");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return -ENXIO;
host = mshci_alloc_host(dev, sizeof(struct cnc1800lsd_host));
if (!host) {
ret = -ENOMEM;
goto out;
}
cardhost = mshci_priv(host);
cardhost->ctrl1 = ioremap(0xB2100140,4);
if (IS_ERR(cardhost->ctrl1)) {
ret = PTR_ERR(cardhost->ctrl1);
goto out;
}
cardhost->ctrl2 = ioremap(0xB2110024,4);
if (IS_ERR(cardhost->ctrl2)) {
ret = PTR_ERR(cardhost->ctrl2);
goto out;
}
cardhost->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cardhost->base)) {
ret = PTR_ERR(cardhost->base);
goto out;
}
*cardhost->ctrl1 = 0xffffffff;
*cardhost->ctrl2 = 0x1;
mdelay(100);
// In the original source, manipulate GPIO here in order to enable WLAN on TIGA_MINI platform.
// But, I won't touch it :)
cardhost->host = host;
cardhost->pdev = pdev;
platform_set_drvdata(pdev,host);
cardhost->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(cardhost->clk)) {
dev_err(&pdev->dev, "no clock associated\n");
ret = -EINVAL;
goto out;
}
clk_prepare_enable(cardhost->clk);
host->ioaddr = cardhost->base;
host->hw_name = "cavium-mshci";
host->ops = &mshci_cnc1800l_ops;
host->quirks = 0;
host->irq = irq;
host->mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ;
host->quirks |= MSHCI_QUIRK_BROKEN_PRESENT_BIT | MSHCI_QUIRK_1BIT_INTERRUPT;
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
ret = mshci_add_host(host);
if(ret){
dev_err(dev, "mshci_add_host() failed\n");
goto out;
}
return 0;
out:
return ret;
}
static int cnc1800lsd_remove(struct platform_device *pdev)
{
struct cnc1800lsd_host *pdata = pdev->dev.platform_data;
struct mshci_host *host = platform_get_drvdata(pdev);
mshci_remove_host(host, 1);
pdata->ctrl2 = 0x0;
return 0;
}
static const struct of_device_id cnc1800lsdio_dt_ids[] = {
{ .compatible = "cavium,cnc1800l-sdio" },
{ }
};
MODULE_DEVICE_TABLE(of, cnc1800lsdio_dt_ids);
static struct platform_driver cnc1800lsd_driver = {
.probe = cnc1800lsd_probe,
.remove = cnc1800lsd_remove,
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = cnc1800lsdio_dt_ids,
},
};
module_platform_driver(cnc1800lsd_driver);
MODULE_DESCRIPTION("Cavium Celestial MSHCI glue");
MODULE_AUTHOR("Scott Shu <sshu@caviumnetworks.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:cnc1800l-mshci");

1742
drivers/mmc/host/mshci.c Normal file

File diff suppressed because it is too large Load Diff

296
drivers/mmc/host/mshci.h Normal file
View File

@ -0,0 +1,296 @@
/*
* linux/drivers/mmc/host/mshci.h
*
* Mobile Storage Host Controller Interface driver
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Based on linux/drivers/mmc/host/sdhci.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef __MSHCI_H
#define __MSHCI_H
#include <linux/scatterlist.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
/* MSHC Internal Registers */
#define MSHCI_CTRL 0x00 /* Control */
#define MSHCI_PWREN 0x04 /* Power-enable */
#define MSHCI_CLKDIV 0x08 /* Clock divider */
#define MSHCI_CLKSRC 0x0C /* Clock source */
#define MSHCI_CLKENA 0x10 /* Clock enable */
#define MSHCI_TMOUT 0x14 /* Timeout */
#define MSHCI_CTYPE 0x18 /* Card type */
#define MSHCI_BLKSIZ 0x1C /* Block Size */
#define MSHCI_BYTCNT 0x20 /* Byte count */
#define MSHCI_INTMSK 0x24 /* Interrupt Mask */
#define MSHCI_CMDARG 0x28 /* Command Argument */
#define MSHCI_CMD 0x2C /* Command */
#define MSHCI_RESP0 0x30 /* Response 0 */
#define MSHCI_RESP1 0x34 /* Response 1 */
#define MSHCI_RESP2 0x38 /* Response 2 */
#define MSHCI_RESP3 0x3C /* Response 3 */
#define MSHCI_MINTSTS 0x40 /* Masked interrupt status */
#define MSHCI_RINTSTS 0x44 /* Raw interrupt status */
#define MSHCI_STATUS 0x48 /* Status */
#define MSHCI_FIFOTH 0x4C /* FIFO threshold */
#define MSHCI_CDETECT 0x50 /* Card detect */
#define MSHCI_WRTPRT 0x54 /* Write protect */
#define MSHCI_GPIO 0x58 /* General Purpose IO */
#define MSHCI_TCBCNT 0x5C /* Transferred CIU byte count */
#define MSHCI_TBBCNT 0x60 /* Transferred host/DMA to/from byte count */
#define MSHCI_DEBNCE 0x64 /* Card detect debounce */
#define MSHCI_USRID 0x68 /* User ID */
#define MSHCI_VERID 0x6C /* Version ID */
#define MSHCI_HCON 0x70 /* Hardware Configuration */
#define MSHCI_UHS_REG 0x74 /* UHS and DDR setting */
#define MSHCI_BMOD 0x80 /* Bus mode register */
#define MSHCI_PLDMND 0x84 /* Poll demand */
#define MSHCI_DBADDR 0x88 /* Descriptor list base address */
#define MSHCI_IDSTS 0x8C /* Internal DMAC status */
#define MSHCI_IDINTEN 0x90 /* Internal DMAC interrupt enable */
#define MSHCI_DSCADDR 0x94 /* Current host descriptor address */
#define MSHCI_BUFADDR 0x98 /* Current host buffer address */
#define MSHCI_CLKSEL 0x9C
#define MSHCI_WAKEUPCON 0xA0 /* Wakeup control register */
#define MSHCI_CLOCKCON 0xA4 /* Clock (delay) control register */
#define MSHCI_FIFODAT 0x100 /* FIFO data read write */
/* Control Register MSHCI_CTRL(offset 0x00) */
#define CTRL_RESET (0x1 << 0) /* Reset DWC_mobile_storage controller */
#define FIFO_RESET (0x1 << 1) /* Reset FIFO */
#define DMA_RESET (0x1 << 2) /* Reset DMA interface */
#define INT_ENABLE (0x1 << 4) /* Global interrupt enable/disable bit */
#define DMA_ENABLE (0x1 << 5) /* DMA transfer mode enable/disable bit */
#define ENABLE_IDMAC (0x1 << 25)
/* Clock Enable Register MSHCI_CLKENA(offset 0x10) */
#define CLK_ENABLE (0x1 << 0)
#define CLK_DISABLE (0x0 << 0)
/* Interrupt Mask Register MSHCI_INTMSK(offset 0x24) */
#define SDIO_INT_ENABLE (0x1 << 16)
/* Interrupt bits */
#define INTMSK_ALL 0xFFFFFFFF
#define INTMSK_CDETECT (0x1 << 0)
#define INTMSK_RE (0x1 << 1)
#define INTMSK_CDONE (0x1 << 2)
#define INTMSK_DTO (0x1 << 3)
#define INTMSK_TXDR (0x1 << 4)
#define INTMSK_RXDR (0x1 << 5)
#define INTMSK_RCRC (0x1 << 6)
#define INTMSK_DCRC (0x1 << 7)
#define INTMSK_RTO (0x1 << 8)
#define INTMSK_DRTO (0x1 << 9)
#define INTMSK_HTO (0x1 << 10)
#define INTMSK_FRUN (0x1 << 11)
#define INTMSK_HLE (0x1 << 12)
#define INTMSK_SBE (0x1 << 13)
#define INTMSK_ACD (0x1 << 14)
#define INTMSK_EBE (0x1 << 15)
#define INTMSK_DMA (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR)
#define INT_SRC_IDMAC (0x0)
#define INT_SRC_MINT (0x1)
/* Command Register MSHCI_CMD(offset 0x2C) */
#define CMD_RESP_EXP_BIT (0x1 << 6)
#define CMD_RESP_LENGTH_BIT (0x1 << 7)
#define CMD_CHECK_CRC_BIT (0x1 << 8)
#define CMD_DATA_EXP_BIT (0x1 << 9)
#define CMD_RW_BIT (0x1 << 10)
#define CMD_TRANSMODE_BIT (0x1 << 11)
#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13)
#define CMD_SEND_CLK_ONLY (0x1 << 21)
#define CMD_STRT_BIT (0x1 << 31)
#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \
CMD_WAIT_PRV_DAT_BIT)
/* Raw Interrupt Register MSHCI_RINTSTS(offset 0x44) */
#define DATA_ERR (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE | \
INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC)
#define DATA_TOUT (INTMSK_HTO | INTMSK_DRTO)
#define DATA_STATUS (DATA_ERR | DATA_TOUT | INTMSK_RXDR | \
INTMSK_TXDR | INTMSK_DTO)
#define CMD_STATUS (INTMSK_RTO | INTMSK_RCRC | INTMSK_CDONE | \
INTMSK_RE)
/* Status Register MSHCI_STATUS(offset 0x48) */
#define FIFO_COUNT (0x1FFF << 17)
#define FIFO_WIDTH (0x4)
/* FIFO Threshold Watermark Register MSHCI_FIFOTH(offset 0x4C) */
#define TX_WMARK (0xFFF << 0)
#define RX_WMARK (0xFFF << 16)
#define MSIZE_MASK (0x7 << 28)
/* DW DMA Mutiple Transaction Size */
#define MSIZE_8 (2 << 28)
/*
* Card Detect Register MSHCI_CDETECT(offset 0x50)
* It assumes there is only one SD slot
*/
#define CARD_PRESENT (0x1 << 0)
/*
* Write Protect Register MSHCI_WRTPRT(offset 0x54)
* It assumes there is only one SD slot
*/
#define WRTPRT_ON (0x1 << 0)
/* Bus Mode Register MSHCI_BMOD(offset 0x80) */
#define BMOD_IDMAC_RESET (0x1 << 1)
#define BMOD_IDMAC_FB (0x1 << 1)
#define BMOD_IDMAC_ENABLE (0x1 << 7)
/* Hardware Configuration Register MSHCI_IDSTS(offset 0x8c) */
#define IDSTS_CES (0x1 << 5)
#define IDSTS_DU (0x1 << 4)
#define IDSTS_FBE (0x1 << 2)
struct mshci_ops;
struct mshci_idmac {
u32 des0;
u32 des1;
u32 des2;
u32 des3;
#define MSHCI_IDMAC_OWN (1 << 31)
#define MSHCI_IDMAC_CH (1 << 4)
#define MSHCI_IDMAC_FS (1 << 3)
#define MSHCI_IDMAC_LD (1 << 2)
#define INTMSK_IDMAC_ERROR (0x214)
};
struct mshci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
unsigned int quirks; /* Deviations from spec. */
/* Controller has no write-protect pin connected with SD card */
#define MSHCI_QUIRK_NO_WP_BIT (1 << 0)
#define MSHCI_QUIRK_BROKEN_CARD_DETECTION (1 << 1)
#define MSHCI_QUIRK_ALWAYS_WRITABLE (1 << 2)
#define MSHCI_QUIRK_BROKEN_PRESENT_BIT (1 << 3)
#define MSHCI_QUIRK_1BIT_INTERRUPT (1 << 4)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
const struct mshci_ops *ops; /* Low level hw interface */
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */
spinlock_t lock; /* Mutex */
int flags; /* Host attributes */
#define MSHCI_USE_IDMA (1 << 1) /* Host is ADMA capable */
#define MSHCI_REQ_USE_DMA (1 << 2) /* Use DMA for this req. */
#define MSHCI_DEVICE_DEAD (1 << 3) /* Device unresponsive */
#define MSHCI_IN_4BIT_MODE (1 << 4) /* bus is in 4-bit mode */
unsigned int version; /* SDHCI spec. version */
unsigned int max_clk; /* Max possible freq (MHz) */
unsigned int timeout_clk; /* Timeout freq (KHz) */
unsigned int clock; /* Current clock (MHz) */
unsigned int clock_to_restore; /* Saved clock for dynamic clock gating (MHz) */
u8 pwr; /* Current voltage */
struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
struct sg_mapping_iter sg_miter; /* SG state for PIO */
unsigned int blocks; /* remaining PIO blocks */
int sg_count; /* Mapped sg entries */
u8 *idma_desc; /* ADMA descriptor table */
u8 *align_buffer; /* Bounce buffer */
dma_addr_t idma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */
struct tasklet_struct card_tasklet; /* Tasklet structures */
struct tasklet_struct finish_tasklet;
struct timer_list timer; /* Timer for timeouts */
struct timer_list clock_timer; /* Timer for clock gating */
u32 fifo_depth;
u32 fifo_threshold;
u32 data_transfered;
u32 error_state;
unsigned long private[0] ____cacheline_aligned;
};
struct mshci_ops {
void (*set_clock)(struct mshci_host *host, unsigned int clock);
int (*enable_dma)(struct mshci_host *host);
unsigned int (*get_max_clock)(struct mshci_host *host);
unsigned int (*get_min_clock)(struct mshci_host *host);
unsigned int (*get_timeout_clock)(struct mshci_host *host);
void (*set_ios)(struct mshci_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *mmc);
void (*init_issue_cmd)(struct mshci_host *host);
void (*init_card)(struct mshci_host *host);
};
static inline void mshci_writel(struct mshci_host *host, u32 val, int reg)
{
writel(val, host->ioaddr + reg);
}
static inline u32 mshci_readl(struct mshci_host *host, int reg)
{
return readl(host->ioaddr + reg);
}
extern struct mshci_host *mshci_alloc_host(struct device *dev,
size_t priv_size);
extern void mshci_free_host(struct mshci_host *host);
static inline void *mshci_priv(struct mshci_host *host)
{
return (void *)host->private;
}
extern int mshci_add_host(struct mshci_host *host);
extern void mshci_remove_host(struct mshci_host *host, int dead);
#ifdef CONFIG_PM
extern int mshci_suspend_host(struct mshci_host *host, pm_message_t state);
extern int mshci_resume_host(struct mshci_host *host);
#endif
#endif /* __MSHCI_H */