diff options
| author | Wei WANG <wei_wang@realsil.com.cn> | 2012-11-09 07:53:35 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-15 20:30:14 -0500 |
| commit | b902dd4ecc2cccdd6c033ba5d959d4d6606e55fb (patch) | |
| tree | 6cbfcd40f223fdcf0c7c348bfc309e3177838231 | |
| parent | 2c94b6452cc6de582584f21066cc5e36d9530c59 (diff) | |
drivers/memstick: Add realtek pcie memstick host driver
Realtek PCI-E Memstick card host driver is used to access Memstick
card, with the help of Realtek PCI-E card reader MFD driver.
Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
Acked-by: Alex Dubov <oakad@yahoo.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/memstick/host/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/memstick/host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/memstick/host/rtsx_pci_ms.c | 641 |
3 files changed, 652 insertions, 0 deletions
diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index cc0997a05171..4f7a17fd1aa7 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig | |||
| @@ -42,3 +42,13 @@ config MEMSTICK_R592 | |||
| 42 | 42 | ||
| 43 | To compile this driver as a module, choose M here: the module will | 43 | To compile this driver as a module, choose M here: the module will |
| 44 | be called r592. | 44 | be called r592. |
| 45 | |||
| 46 | config MEMSTICK_REALTEK_PCI | ||
| 47 | tristate "Realtek PCI-E Memstick Card Interface Driver" | ||
| 48 | depends on MFD_RTSX_PCI | ||
| 49 | help | ||
| 50 | Say Y here to include driver code to support Memstick card interface | ||
| 51 | of Realtek PCI-E card reader | ||
| 52 | |||
| 53 | To compile this driver as a module, choose M here: the module will | ||
| 54 | be called rtsx_pci_ms. | ||
diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index 31ba8d378e46..af3459d7686e 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile | |||
| @@ -5,3 +5,4 @@ | |||
| 5 | obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o | 5 | obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o |
| 6 | obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o | 6 | obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o |
| 7 | obj-$(CONFIG_MEMSTICK_R592) += r592.o | 7 | obj-$(CONFIG_MEMSTICK_R592) += r592.o |
| 8 | obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o | ||
diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c new file mode 100644 index 000000000000..f5ddb82dadb7 --- /dev/null +++ b/drivers/memstick/host/rtsx_pci_ms.c | |||
| @@ -0,0 +1,641 @@ | |||
| 1 | /* Realtek PCI-Express Memstick Card Interface driver | ||
| 2 | * | ||
| 3 | * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License as published by the | ||
| 7 | * Free Software Foundation; either version 2, or (at your option) any | ||
| 8 | * later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along | ||
| 16 | * with this program; if not, see <http://www.gnu.org/licenses/>. | ||
| 17 | * | ||
| 18 | * Author: | ||
| 19 | * Wei WANG <wei_wang@realsil.com.cn> | ||
| 20 | * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/highmem.h> | ||
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/memstick.h> | ||
| 28 | #include <linux/mfd/rtsx_pci.h> | ||
| 29 | #include <asm/unaligned.h> | ||
| 30 | |||
| 31 | struct realtek_pci_ms { | ||
| 32 | struct platform_device *pdev; | ||
| 33 | struct rtsx_pcr *pcr; | ||
| 34 | struct memstick_host *msh; | ||
| 35 | struct memstick_request *req; | ||
| 36 | |||
| 37 | struct mutex host_mutex; | ||
| 38 | struct work_struct handle_req; | ||
| 39 | |||
| 40 | u8 ssc_depth; | ||
| 41 | unsigned int clock; | ||
| 42 | unsigned char ifmode; | ||
| 43 | bool eject; | ||
| 44 | }; | ||
| 45 | |||
| 46 | static inline struct device *ms_dev(struct realtek_pci_ms *host) | ||
| 47 | { | ||
| 48 | return &(host->pdev->dev); | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline void ms_clear_error(struct realtek_pci_ms *host) | ||
| 52 | { | ||
| 53 | rtsx_pci_write_register(host->pcr, CARD_STOP, | ||
| 54 | MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR); | ||
| 55 | } | ||
| 56 | |||
| 57 | #ifdef DEBUG | ||
| 58 | |||
| 59 | static void ms_print_debug_regs(struct realtek_pci_ms *host) | ||
| 60 | { | ||
| 61 | struct rtsx_pcr *pcr = host->pcr; | ||
| 62 | u16 i; | ||
| 63 | u8 *ptr; | ||
| 64 | |||
| 65 | /* Print MS host internal registers */ | ||
| 66 | rtsx_pci_init_cmd(pcr); | ||
| 67 | for (i = 0xFD40; i <= 0xFD44; i++) | ||
| 68 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); | ||
| 69 | for (i = 0xFD52; i <= 0xFD69; i++) | ||
| 70 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); | ||
| 71 | rtsx_pci_send_cmd(pcr, 100); | ||
| 72 | |||
| 73 | ptr = rtsx_pci_get_cmd_data(pcr); | ||
| 74 | for (i = 0xFD40; i <= 0xFD44; i++) | ||
| 75 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); | ||
| 76 | for (i = 0xFD52; i <= 0xFD69; i++) | ||
| 77 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); | ||
| 78 | } | ||
| 79 | |||
| 80 | #else | ||
| 81 | |||
| 82 | #define ms_print_debug_regs(host) | ||
| 83 | |||
| 84 | #endif | ||
| 85 | |||
| 86 | static int ms_power_on(struct realtek_pci_ms *host) | ||
| 87 | { | ||
| 88 | struct rtsx_pcr *pcr = host->pcr; | ||
| 89 | int err; | ||
| 90 | |||
| 91 | rtsx_pci_init_cmd(pcr); | ||
| 92 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL); | ||
| 93 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, | ||
| 94 | CARD_SHARE_MASK, CARD_SHARE_48_MS); | ||
| 95 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, | ||
| 96 | MS_CLK_EN, MS_CLK_EN); | ||
| 97 | err = rtsx_pci_send_cmd(pcr, 100); | ||
| 98 | if (err < 0) | ||
| 99 | return err; | ||
| 100 | |||
| 101 | err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD); | ||
| 102 | if (err < 0) | ||
| 103 | return err; | ||
| 104 | |||
| 105 | err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD); | ||
| 106 | if (err < 0) | ||
| 107 | return err; | ||
| 108 | |||
| 109 | /* Wait ms power stable */ | ||
| 110 | msleep(150); | ||
| 111 | |||
| 112 | err = rtsx_pci_write_register(pcr, CARD_OE, | ||
| 113 | MS_OUTPUT_EN, MS_OUTPUT_EN); | ||
| 114 | if (err < 0) | ||
| 115 | return err; | ||
| 116 | |||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static int ms_power_off(struct realtek_pci_ms *host) | ||
| 121 | { | ||
| 122 | struct rtsx_pcr *pcr = host->pcr; | ||
| 123 | int err; | ||
| 124 | |||
| 125 | rtsx_pci_init_cmd(pcr); | ||
| 126 | |||
| 127 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0); | ||
| 128 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); | ||
| 129 | |||
| 130 | err = rtsx_pci_send_cmd(pcr, 100); | ||
| 131 | if (err < 0) | ||
| 132 | return err; | ||
| 133 | |||
| 134 | err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); | ||
| 135 | if (err < 0) | ||
| 136 | return err; | ||
| 137 | |||
| 138 | return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); | ||
| 139 | } | ||
| 140 | |||
| 141 | static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir, | ||
| 142 | u8 tpc, u8 cfg, struct scatterlist *sg) | ||
| 143 | { | ||
| 144 | struct rtsx_pcr *pcr = host->pcr; | ||
| 145 | int err; | ||
| 146 | unsigned int length = sg->length; | ||
| 147 | u16 sec_cnt = (u16)(length / 512); | ||
| 148 | u8 val, trans_mode, dma_dir; | ||
| 149 | |||
| 150 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n", | ||
| 151 | __func__, tpc, (data_dir == READ) ? "READ" : "WRITE", | ||
| 152 | length); | ||
| 153 | |||
| 154 | if (data_dir == READ) { | ||
| 155 | dma_dir = DMA_DIR_FROM_CARD; | ||
| 156 | trans_mode = MS_TM_AUTO_READ; | ||
| 157 | } else { | ||
| 158 | dma_dir = DMA_DIR_TO_CARD; | ||
| 159 | trans_mode = MS_TM_AUTO_WRITE; | ||
| 160 | } | ||
| 161 | |||
| 162 | rtsx_pci_init_cmd(pcr); | ||
| 163 | |||
| 164 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); | ||
| 165 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H, | ||
| 166 | 0xFF, (u8)(sec_cnt >> 8)); | ||
| 167 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L, | ||
| 168 | 0xFF, (u8)sec_cnt); | ||
| 169 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); | ||
| 170 | |||
| 171 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, | ||
| 172 | DMA_DONE_INT, DMA_DONE_INT); | ||
| 173 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24)); | ||
| 174 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16)); | ||
| 175 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8)); | ||
| 176 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length); | ||
| 177 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, | ||
| 178 | 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); | ||
| 179 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CAR | ||
