101d95843SSteven J. Hill /* 201d95843SSteven J. Hill * Driver for MMC and SSD cards for Cavium OCTEON SOCs. 301d95843SSteven J. Hill * 401d95843SSteven J. Hill * This file is subject to the terms and conditions of the GNU General Public 501d95843SSteven J. Hill * License. See the file "COPYING" in the main directory of this archive 601d95843SSteven J. Hill * for more details. 701d95843SSteven J. Hill * 801d95843SSteven J. Hill * Copyright (C) 2012-2017 Cavium Inc. 901d95843SSteven J. Hill */ 1001d95843SSteven J. Hill #include <linux/dma-mapping.h> 1101d95843SSteven J. Hill #include <linux/gpio/consumer.h> 1201d95843SSteven J. Hill #include <linux/interrupt.h> 1301d95843SSteven J. Hill #include <linux/mmc/mmc.h> 1401d95843SSteven J. Hill #include <linux/mmc/slot-gpio.h> 1501d95843SSteven J. Hill #include <linux/module.h> 1601d95843SSteven J. Hill #include <linux/of_platform.h> 1701d95843SSteven J. Hill #include <asm/octeon/octeon.h> 1801d95843SSteven J. Hill #include "cavium.h" 1901d95843SSteven J. Hill 2001d95843SSteven J. Hill #define CVMX_MIO_BOOT_CTL CVMX_ADD_IO_SEG(0x00011800000000D0ull) 2101d95843SSteven J. Hill 2201d95843SSteven J. Hill /* 2301d95843SSteven J. Hill * The l2c* functions below are used for the EMMC-17978 workaround. 2401d95843SSteven J. Hill * 2501d95843SSteven J. Hill * Due to a bug in the design of the MMC bus hardware, the 2nd to last 2601d95843SSteven J. Hill * cache block of a DMA read must be locked into the L2 Cache. 2701d95843SSteven J. Hill * Otherwise, data corruption may occur. 2801d95843SSteven J. Hill */ 2901d95843SSteven J. Hill static inline void *phys_to_ptr(u64 address) 3001d95843SSteven J. Hill { 3101d95843SSteven J. Hill return (void *)(address | (1ull << 63)); /* XKPHYS */ 3201d95843SSteven J. Hill } 3301d95843SSteven J. Hill 3401d95843SSteven J. Hill /* 3501d95843SSteven J. Hill * Lock a single line into L2. The line is zeroed before locking 3601d95843SSteven J. Hill * to make sure no dram accesses are made. 3701d95843SSteven J. Hill */ 3801d95843SSteven J. Hill static void l2c_lock_line(u64 addr) 3901d95843SSteven J. Hill { 4001d95843SSteven J. Hill char *addr_ptr = phys_to_ptr(addr); 4101d95843SSteven J. Hill 4201d95843SSteven J. Hill asm volatile ( 4301d95843SSteven J. Hill "cache 31, %[line]" /* Unlock the line */ 4401d95843SSteven J. Hill ::[line] "m" (*addr_ptr)); 4501d95843SSteven J. Hill } 4601d95843SSteven J. Hill 4701d95843SSteven J. Hill /* Unlock a single line in the L2 cache. */ 4801d95843SSteven J. Hill static void l2c_unlock_line(u64 addr) 4901d95843SSteven J. Hill { 5001d95843SSteven J. Hill char *addr_ptr = phys_to_ptr(addr); 5101d95843SSteven J. Hill 5201d95843SSteven J. Hill asm volatile ( 5301d95843SSteven J. Hill "cache 23, %[line]" /* Unlock the line */ 5401d95843SSteven J. Hill ::[line] "m" (*addr_ptr)); 5501d95843SSteven J. Hill } 5601d95843SSteven J. Hill 5701d95843SSteven J. Hill /* Locks a memory region in the L2 cache. */ 5801d95843SSteven J. Hill static void l2c_lock_mem_region(u64 start, u64 len) 5901d95843SSteven J. Hill { 6001d95843SSteven J. Hill u64 end; 6101d95843SSteven J. Hill 6201d95843SSteven J. Hill /* Round start/end to cache line boundaries */ 6301d95843SSteven J. Hill end = ALIGN(start + len - 1, CVMX_CACHE_LINE_SIZE); 6401d95843SSteven J. Hill start = ALIGN(start, CVMX_CACHE_LINE_SIZE); 6501d95843SSteven J. Hill 6601d95843SSteven J. Hill while (start <= end) { 6701d95843SSteven J. Hill l2c_lock_line(start); 6801d95843SSteven J. Hill start += CVMX_CACHE_LINE_SIZE; 6901d95843SSteven J. Hill } 7001d95843SSteven J. Hill asm volatile("sync"); 7101d95843SSteven J. Hill } 7201d95843SSteven J. Hill 7301d95843SSteven J. Hill /* Unlock a memory region in the L2 cache. */ 7401d95843SSteven J. Hill static void l2c_unlock_mem_region(u64 start, u64 len) 7501d95843SSteven J. Hill { 7601d95843SSteven J. Hill u64 end; 7701d95843SSteven J. Hill 7801d95843SSteven J. Hill /* Round start/end to cache line boundaries */ 7901d95843SSteven J. Hill end = ALIGN(start + len - 1, CVMX_CACHE_LINE_SIZE); 8001d95843SSteven J. Hill start = ALIGN(start, CVMX_CACHE_LINE_SIZE); 8101d95843SSteven J. Hill 8201d95843SSteven J. Hill while (start <= end) { 8301d95843SSteven J. Hill l2c_unlock_line(start); 8401d95843SSteven J. Hill start += CVMX_CACHE_LINE_SIZE; 8501d95843SSteven J. Hill } 8601d95843SSteven J. Hill } 8701d95843SSteven J. Hill 8801d95843SSteven J. Hill static void octeon_mmc_acquire_bus(struct cvm_mmc_host *host) 8901d95843SSteven J. Hill { 9001d95843SSteven J. Hill if (!host->has_ciu3) { 9101d95843SSteven J. Hill down(&octeon_bootbus_sem); 9201d95843SSteven J. Hill /* For CN70XX, switch the MMC controller onto the bus. */ 9301d95843SSteven J. Hill if (OCTEON_IS_MODEL(OCTEON_CN70XX)) 9401d95843SSteven J. Hill writeq(0, (void __iomem *)CVMX_MIO_BOOT_CTL); 9501d95843SSteven J. Hill } else { 9601d95843SSteven J. Hill down(&host->mmc_serializer); 9701d95843SSteven J. Hill } 9801d95843SSteven J. Hill } 9901d95843SSteven J. Hill 10001d95843SSteven J. Hill static void octeon_mmc_release_bus(struct cvm_mmc_host *host) 10101d95843SSteven J. Hill { 10201d95843SSteven J. Hill if (!host->has_ciu3) 10301d95843SSteven J. Hill up(&octeon_bootbus_sem); 10401d95843SSteven J. Hill else 10501d95843SSteven J. Hill up(&host->mmc_serializer); 10601d95843SSteven J. Hill } 10701d95843SSteven J. Hill 10801d95843SSteven J. Hill static void octeon_mmc_int_enable(struct cvm_mmc_host *host, u64 val) 10901d95843SSteven J. Hill { 11001d95843SSteven J. Hill writeq(val, host->base + MIO_EMM_INT(host)); 111aca69344SDavid Daney if (!host->has_ciu3) 11201d95843SSteven J. Hill writeq(val, host->base + MIO_EMM_INT_EN(host)); 11301d95843SSteven J. Hill } 11401d95843SSteven J. Hill 11501d95843SSteven J. Hill static void octeon_mmc_set_shared_power(struct cvm_mmc_host *host, int dir) 11601d95843SSteven J. Hill { 11701d95843SSteven J. Hill if (dir == 0) 11801d95843SSteven J. Hill if (!atomic_dec_return(&host->shared_power_users)) 11901d95843SSteven J. Hill gpiod_set_value_cansleep(host->global_pwr_gpiod, 0); 12001d95843SSteven J. Hill if (dir == 1) 12101d95843SSteven J. Hill if (atomic_inc_return(&host->shared_power_users) == 1) 12201d95843SSteven J. Hill gpiod_set_value_cansleep(host->global_pwr_gpiod, 1); 12301d95843SSteven J. Hill } 12401d95843SSteven J. Hill 12501d95843SSteven J. Hill static void octeon_mmc_dmar_fixup(struct cvm_mmc_host *host, 12601d95843SSteven J. Hill struct mmc_command *cmd, 12701d95843SSteven J. Hill struct mmc_data *data, 12801d95843SSteven J. Hill u64 addr) 12901d95843SSteven J. Hill { 13001d95843SSteven J. Hill if (cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) 13101d95843SSteven J. Hill return; 13201d95843SSteven J. Hill if (data->blksz * data->blocks <= 1024) 13301d95843SSteven J. Hill return; 13401d95843SSteven J. Hill 13501d95843SSteven J. Hill host->n_minus_one = addr + (data->blksz * data->blocks) - 1024; 13601d95843SSteven J. Hill l2c_lock_mem_region(host->n_minus_one, 512); 13701d95843SSteven J. Hill } 13801d95843SSteven J. Hill 13901d95843SSteven J. Hill static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host) 14001d95843SSteven J. Hill { 14101d95843SSteven J. Hill if (!host->n_minus_one) 14201d95843SSteven J. Hill return; 14301d95843SSteven J. Hill l2c_unlock_mem_region(host->n_minus_one, 512); 14401d95843SSteven J. Hill host->n_minus_one = 0; 14501d95843SSteven J. Hill } 14601d95843SSteven J. Hill 14701d95843SSteven J. Hill static int octeon_mmc_probe(struct platform_device *pdev) 14801d95843SSteven J. Hill { 14901d95843SSteven J. Hill struct device_node *cn, *node = pdev->dev.of_node; 15001d95843SSteven J. Hill struct cvm_mmc_host *host; 15101d95843SSteven J. Hill struct resource *res; 15201d95843SSteven J. Hill void __iomem *base; 15301d95843SSteven J. Hill int mmc_irq[9]; 15401d95843SSteven J. Hill int i, ret = 0; 15501d95843SSteven J. Hill u64 val; 15601d95843SSteven J. Hill 15701d95843SSteven J. Hill host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 15801d95843SSteven J. Hill if (!host) 15901d95843SSteven J. Hill return -ENOMEM; 16001d95843SSteven J. Hill 16101d95843SSteven J. Hill spin_lock_init(&host->irq_handler_lock); 16201d95843SSteven J. Hill sema_init(&host->mmc_serializer, 1); 16301d95843SSteven J. Hill 16401d95843SSteven J. Hill host->dev = &pdev->dev; 16501d95843SSteven J. Hill host->acquire_bus = octeon_mmc_acquire_bus; 16601d95843SSteven J. Hill host->release_bus = octeon_mmc_release_bus; 16701d95843SSteven J. Hill host->int_enable = octeon_mmc_int_enable; 16801d95843SSteven J. Hill host->set_shared_power = octeon_mmc_set_shared_power; 16901d95843SSteven J. Hill if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || 17001d95843SSteven J. Hill OCTEON_IS_MODEL(OCTEON_CNF7XXX)) { 17101d95843SSteven J. Hill host->dmar_fixup = octeon_mmc_dmar_fixup; 17201d95843SSteven J. Hill host->dmar_fixup_done = octeon_mmc_dmar_fixup_done; 17301d95843SSteven J. Hill } 17401d95843SSteven J. Hill 17501d95843SSteven J. Hill host->sys_freq = octeon_get_io_clock_rate(); 17601d95843SSteven J. Hill 17701d95843SSteven J. Hill if (of_device_is_compatible(node, "cavium,octeon-7890-mmc")) { 17801d95843SSteven J. Hill host->big_dma_addr = true; 17901d95843SSteven J. Hill host->need_irq_handler_lock = true; 18001d95843SSteven J. Hill host->has_ciu3 = true; 18101d95843SSteven J. Hill host->use_sg = true; 18201d95843SSteven J. Hill /* 18301d95843SSteven J. Hill * First seven are the EMM_INT bits 0..6, then two for 18401d95843SSteven J. Hill * the EMM_DMA_INT bits 18501d95843SSteven J. Hill */ 18601d95843SSteven J. Hill for (i = 0; i < 9; i++) { 18701d95843SSteven J. Hill mmc_irq[i] = platform_get_irq(pdev, i); 18801d95843SSteven J. Hill if (mmc_irq[i] < 0) 18901d95843SSteven J. Hill return mmc_irq[i]; 19001d95843SSteven J. Hill 19101d95843SSteven J. Hill /* work around legacy u-boot device trees */ 19201d95843SSteven J. Hill irq_set_irq_type(mmc_irq[i], IRQ_TYPE_EDGE_RISING); 19301d95843SSteven J. Hill } 19401d95843SSteven J. Hill } else { 19501d95843SSteven J. Hill host->big_dma_addr = false; 19601d95843SSteven J. Hill host->need_irq_handler_lock = false; 19701d95843SSteven J. Hill host->has_ciu3 = false; 19801d95843SSteven J. Hill /* First one is EMM second DMA */ 19901d95843SSteven J. Hill for (i = 0; i < 2; i++) { 20001d95843SSteven J. Hill mmc_irq[i] = platform_get_irq(pdev, i); 20101d95843SSteven J. Hill if (mmc_irq[i] < 0) 20201d95843SSteven J. Hill return mmc_irq[i]; 20301d95843SSteven J. Hill } 20401d95843SSteven J. Hill } 20501d95843SSteven J. Hill 20601d95843SSteven J. Hill host->last_slot = -1; 20701d95843SSteven J. Hill 20801d95843SSteven J. Hill res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 20901d95843SSteven J. Hill if (!res) { 21001d95843SSteven J. Hill dev_err(&pdev->dev, "Platform resource[0] is missing\n"); 21101d95843SSteven J. Hill return -ENXIO; 21201d95843SSteven J. Hill } 21301d95843SSteven J. Hill base = devm_ioremap_resource(&pdev->dev, res); 21401d95843SSteven J. Hill if (IS_ERR(base)) 21501d95843SSteven J. Hill return PTR_ERR(base); 21601d95843SSteven J. Hill host->base = (void __iomem *)base; 21701d95843SSteven J. Hill host->reg_off = 0; 21801d95843SSteven J. Hill 21901d95843SSteven J. Hill res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 22001d95843SSteven J. Hill if (!res) { 22101d95843SSteven J. Hill dev_err(&pdev->dev, "Platform resource[1] is missing\n"); 22201d95843SSteven J. Hill return -EINVAL; 22301d95843SSteven J. Hill } 22401d95843SSteven J. Hill base = devm_ioremap_resource(&pdev->dev, res); 22501d95843SSteven J. Hill if (IS_ERR(base)) 22601d95843SSteven J. Hill return PTR_ERR(base); 22701d95843SSteven J. Hill host->dma_base = (void __iomem *)base; 22801d95843SSteven J. Hill /* 22901d95843SSteven J. Hill * To keep the register addresses shared we intentionaly use 23001d95843SSteven J. Hill * a negative offset here, first register used on Octeon therefore 23101d95843SSteven J. Hill * starts at 0x20 (MIO_EMM_DMA_CFG). 23201d95843SSteven J. Hill */ 23301d95843SSteven J. Hill host->reg_off_dma = -0x20; 23401d95843SSteven J. Hill 23501d95843SSteven J. Hill ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); 23601d95843SSteven J. Hill if (ret) 23701d95843SSteven J. Hill return ret; 23801d95843SSteven J. Hill 23901d95843SSteven J. Hill /* 24001d95843SSteven J. Hill * Clear out any pending interrupts that may be left over from 24101d95843SSteven J. Hill * bootloader. 24201d95843SSteven J. Hill */ 24301d95843SSteven J. Hill val = readq(host->base + MIO_EMM_INT(host)); 24401d95843SSteven J. Hill writeq(val, host->base + MIO_EMM_INT(host)); 24501d95843SSteven J. Hill 24601d95843SSteven J. Hill if (host->has_ciu3) { 24701d95843SSteven J. Hill /* Only CMD_DONE, DMA_DONE, CMD_ERR, DMA_ERR */ 24801d95843SSteven J. Hill for (i = 1; i <= 4; i++) { 24901d95843SSteven J. Hill ret = devm_request_irq(&pdev->dev, mmc_irq[i], 25001d95843SSteven J. Hill cvm_mmc_interrupt, 25101d95843SSteven J. Hill 0, cvm_mmc_irq_names[i], host); 25201d95843SSteven J. Hill if (ret < 0) { 25301d95843SSteven J. Hill dev_err(&pdev->dev, "Error: devm_request_irq %d\n", 25401d95843SSteven J. Hill mmc_irq[i]); 25501d95843SSteven J. Hill return ret; 25601d95843SSteven J. Hill } 25701d95843SSteven J. Hill } 25801d95843SSteven J. Hill } else { 25901d95843SSteven J. Hill ret = devm_request_irq(&pdev->dev, mmc_irq[0], 26001d95843SSteven J. Hill cvm_mmc_interrupt, 0, KBUILD_MODNAME, 26101d95843SSteven J. Hill host); 26201d95843SSteven J. Hill if (ret < 0) { 26301d95843SSteven J. Hill dev_err(&pdev->dev, "Error: devm_request_irq %d\n", 26401d95843SSteven J. Hill mmc_irq[0]); 26501d95843SSteven J. Hill return ret; 26601d95843SSteven J. Hill } 26701d95843SSteven J. Hill } 26801d95843SSteven J. Hill 26901d95843SSteven J. Hill host->global_pwr_gpiod = devm_gpiod_get_optional(&pdev->dev, 270899e4aadSDavid Daney "power", 27101d95843SSteven J. Hill GPIOD_OUT_HIGH); 27201d95843SSteven J. Hill if (IS_ERR(host->global_pwr_gpiod)) { 27301d95843SSteven J. Hill dev_err(&pdev->dev, "Invalid power GPIO\n"); 27401d95843SSteven J. Hill return PTR_ERR(host->global_pwr_gpiod); 27501d95843SSteven J. Hill } 27601d95843SSteven J. Hill 27701d95843SSteven J. Hill platform_set_drvdata(pdev, host); 27801d95843SSteven J. Hill 27901d95843SSteven J. Hill i = 0; 28001d95843SSteven J. Hill for_each_child_of_node(node, cn) { 28101d95843SSteven J. Hill host->slot_pdev[i] = 28201d95843SSteven J. Hill of_platform_device_create(cn, NULL, &pdev->dev); 28301d95843SSteven J. Hill if (!host->slot_pdev[i]) { 28401d95843SSteven J. Hill i++; 28501d95843SSteven J. Hill continue; 28601d95843SSteven J. Hill } 28701d95843SSteven J. Hill ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host); 28801d95843SSteven J. Hill if (ret) { 28901d95843SSteven J. Hill dev_err(&pdev->dev, "Error populating slots\n"); 29001d95843SSteven J. Hill octeon_mmc_set_shared_power(host, 0); 2918fb83b14SJan Glauber goto error; 29201d95843SSteven J. Hill } 29301d95843SSteven J. Hill i++; 29401d95843SSteven J. Hill } 29501d95843SSteven J. Hill return 0; 2968fb83b14SJan Glauber 2978fb83b14SJan Glauber error: 2988fb83b14SJan Glauber for (i = 0; i < CAVIUM_MAX_MMC; i++) { 2998fb83b14SJan Glauber if (host->slot[i]) 3008fb83b14SJan Glauber cvm_mmc_of_slot_remove(host->slot[i]); 3018fb83b14SJan Glauber if (host->slot_pdev[i]) 3028fb83b14SJan Glauber of_platform_device_destroy(&host->slot_pdev[i]->dev, NULL); 3038fb83b14SJan Glauber } 3048fb83b14SJan Glauber return ret; 30501d95843SSteven J. Hill } 30601d95843SSteven J. Hill 30701d95843SSteven J. Hill static int octeon_mmc_remove(struct platform_device *pdev) 30801d95843SSteven J. Hill { 30901d95843SSteven J. Hill struct cvm_mmc_host *host = platform_get_drvdata(pdev); 31001d95843SSteven J. Hill u64 dma_cfg; 31101d95843SSteven J. Hill int i; 31201d95843SSteven J. Hill 31301d95843SSteven J. Hill for (i = 0; i < CAVIUM_MAX_MMC; i++) 31401d95843SSteven J. Hill if (host->slot[i]) 31501d95843SSteven J. Hill cvm_mmc_of_slot_remove(host->slot[i]); 31601d95843SSteven J. Hill 31701d95843SSteven J. Hill dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); 31801d95843SSteven J. Hill dma_cfg &= ~MIO_EMM_DMA_CFG_EN; 31901d95843SSteven J. Hill writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host)); 32001d95843SSteven J. Hill 32101d95843SSteven J. Hill octeon_mmc_set_shared_power(host, 0); 32201d95843SSteven J. Hill return 0; 32301d95843SSteven J. Hill } 32401d95843SSteven J. Hill 32501d95843SSteven J. Hill static const struct of_device_id octeon_mmc_match[] = { 32601d95843SSteven J. Hill { 32701d95843SSteven J. Hill .compatible = "cavium,octeon-6130-mmc", 32801d95843SSteven J. Hill }, 32901d95843SSteven J. Hill { 33001d95843SSteven J. Hill .compatible = "cavium,octeon-7890-mmc", 33101d95843SSteven J. Hill }, 33201d95843SSteven J. Hill {}, 33301d95843SSteven J. Hill }; 33401d95843SSteven J. Hill MODULE_DEVICE_TABLE(of, octeon_mmc_match); 33501d95843SSteven J. Hill 33601d95843SSteven J. Hill static struct platform_driver octeon_mmc_driver = { 33701d95843SSteven J. Hill .probe = octeon_mmc_probe, 33801d95843SSteven J. Hill .remove = octeon_mmc_remove, 33901d95843SSteven J. Hill .driver = { 34001d95843SSteven J. Hill .name = KBUILD_MODNAME, 34101d95843SSteven J. Hill .of_match_table = octeon_mmc_match, 34201d95843SSteven J. Hill }, 34301d95843SSteven J. Hill }; 34401d95843SSteven J. Hill 345a814a14eSAxel Lin module_platform_driver(octeon_mmc_driver); 34601d95843SSteven J. Hill 34701d95843SSteven J. Hill MODULE_AUTHOR("Cavium Inc. <support@cavium.com>"); 34801d95843SSteven J. Hill MODULE_DESCRIPTION("Low-level driver for Cavium OCTEON MMC/SSD card"); 34901d95843SSteven J. Hill MODULE_LICENSE("GPL"); 350