Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de> |
| 4 | * |
| 5 | * Driver for Alcor Micro AU6601 and AU6621 controllers |
| 6 | */ |
| 7 | |
| 8 | /* Note: this driver was created without any documentation. Based |
| 9 | * on sniffing, testing and in some cases mimic of original driver. |
| 10 | * As soon as some one with documentation or more experience in SD/MMC, or |
| 11 | * reverse engineering then me, please review this driver and question every |
| 12 | * thing what I did. 2018 Oleksij Rempel <linux@rempel-privat.de> |
| 13 | */ |
| 14 | |
| 15 | #include <linux/delay.h> |
| 16 | #include <linux/pci.h> |
| 17 | #include <linux/module.h> |
| 18 | #include <linux/io.h> |
| 19 | #include <linux/pm.h> |
| 20 | #include <linux/irq.h> |
| 21 | #include <linux/interrupt.h> |
| 22 | #include <linux/platform_device.h> |
| 23 | |
| 24 | #include <linux/mmc/host.h> |
| 25 | #include <linux/mmc/mmc.h> |
| 26 | |
| 27 | #include <linux/alcor_pci.h> |
| 28 | |
| 29 | enum alcor_cookie { |
| 30 | COOKIE_UNMAPPED, |
| 31 | COOKIE_PRE_MAPPED, |
| 32 | COOKIE_MAPPED, |
| 33 | }; |
| 34 | |
| 35 | struct alcor_pll_conf { |
| 36 | unsigned int clk_src_freq; |
| 37 | unsigned int clk_src_reg; |
| 38 | unsigned int min_div; |
| 39 | unsigned int max_div; |
| 40 | }; |
| 41 | |
| 42 | struct alcor_sdmmc_host { |
| 43 | struct device *dev; |
| 44 | struct alcor_pci_priv *alcor_pci; |
| 45 | |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 46 | struct mmc_request *mrq; |
| 47 | struct mmc_command *cmd; |
| 48 | struct mmc_data *data; |
| 49 | unsigned int dma_on:1; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 50 | |
| 51 | struct mutex cmd_mutex; |
| 52 | |
| 53 | struct delayed_work timeout_work; |
| 54 | |
| 55 | struct sg_mapping_iter sg_miter; /* SG state for PIO */ |
| 56 | struct scatterlist *sg; |
| 57 | unsigned int blocks; /* remaining PIO blocks */ |
| 58 | int sg_count; |
| 59 | |
| 60 | u32 irq_status_sd; |
| 61 | unsigned char cur_power_mode; |
| 62 | }; |
| 63 | |
| 64 | static const struct alcor_pll_conf alcor_pll_cfg[] = { |
| 65 | /* MHZ, CLK src, max div, min div */ |
| 66 | { 31250000, AU6601_CLK_31_25_MHZ, 1, 511}, |
| 67 | { 48000000, AU6601_CLK_48_MHZ, 1, 511}, |
| 68 | {125000000, AU6601_CLK_125_MHZ, 1, 511}, |
| 69 | {384000000, AU6601_CLK_384_MHZ, 1, 511}, |
| 70 | }; |
| 71 | |
| 72 | static inline void alcor_rmw8(struct alcor_sdmmc_host *host, unsigned int addr, |
| 73 | u8 clear, u8 set) |
| 74 | { |
| 75 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 76 | u32 var; |
| 77 | |
| 78 | var = alcor_read8(priv, addr); |
| 79 | var &= ~clear; |
| 80 | var |= set; |
| 81 | alcor_write8(priv, var, addr); |
| 82 | } |
| 83 | |
| 84 | /* As soon as irqs are masked, some status updates may be missed. |
| 85 | * Use this with care. |
| 86 | */ |
| 87 | static inline void alcor_mask_sd_irqs(struct alcor_sdmmc_host *host) |
| 88 | { |
| 89 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 90 | |
| 91 | alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); |
| 92 | } |
| 93 | |
| 94 | static inline void alcor_unmask_sd_irqs(struct alcor_sdmmc_host *host) |
| 95 | { |
| 96 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 97 | |
| 98 | alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK | |
| 99 | AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE | |
| 100 | AU6601_INT_OVER_CURRENT_ERR, |
| 101 | AU6601_REG_INT_ENABLE); |
| 102 | } |
| 103 | |
| 104 | static void alcor_reset(struct alcor_sdmmc_host *host, u8 val) |
| 105 | { |
| 106 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 107 | int i; |
| 108 | |
| 109 | alcor_write8(priv, val | AU6601_BUF_CTRL_RESET, |
| 110 | AU6601_REG_SW_RESET); |
| 111 | for (i = 0; i < 100; i++) { |
| 112 | if (!(alcor_read8(priv, AU6601_REG_SW_RESET) & val)) |
| 113 | return; |
| 114 | udelay(50); |
| 115 | } |
| 116 | dev_err(host->dev, "%s: timeout\n", __func__); |
| 117 | } |
| 118 | |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 119 | /* |
| 120 | * Perform DMA I/O of a single page. |
| 121 | */ |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 122 | static void alcor_data_set_dma(struct alcor_sdmmc_host *host) |
| 123 | { |
| 124 | struct alcor_pci_priv *priv = host->alcor_pci; |
YueHaibing | 566b602 | 2018-12-07 06:34:47 +0000 | [diff] [blame] | 125 | u32 addr; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 126 | |
| 127 | if (!host->sg_count) |
| 128 | return; |
| 129 | |
| 130 | if (!host->sg) { |
| 131 | dev_err(host->dev, "have blocks, but no SG\n"); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | if (!sg_dma_len(host->sg)) { |
| 136 | dev_err(host->dev, "DMA SG len == 0\n"); |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | |
| 141 | addr = (u32)sg_dma_address(host->sg); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 142 | |
| 143 | alcor_write32(priv, addr, AU6601_REG_SDMA_ADDR); |
| 144 | host->sg = sg_next(host->sg); |
| 145 | host->sg_count--; |
| 146 | } |
| 147 | |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 148 | static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host) |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 149 | { |
| 150 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 151 | struct mmc_data *data = host->data; |
| 152 | u8 ctrl = 0; |
| 153 | |
| 154 | if (data->flags & MMC_DATA_WRITE) |
| 155 | ctrl |= AU6601_DATA_WRITE; |
| 156 | |
| 157 | if (data->host_cookie == COOKIE_MAPPED) { |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 158 | /* |
| 159 | * For DMA transfers, this function is called just once, |
| 160 | * at the start of the operation. The hardware can only |
| 161 | * perform DMA I/O on a single page at a time, so here |
| 162 | * we kick off the transfer with the first page, and expect |
| 163 | * subsequent pages to be transferred upon IRQ events |
| 164 | * indicating that the single-page DMA was completed. |
| 165 | */ |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 166 | alcor_data_set_dma(host); |
| 167 | ctrl |= AU6601_DATA_DMA_MODE; |
| 168 | host->dma_on = 1; |
| 169 | alcor_write32(priv, data->sg_count * 0x1000, |
| 170 | AU6601_REG_BLOCK_SIZE); |
| 171 | } else { |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 172 | /* |
| 173 | * For PIO transfers, we break down each operation |
| 174 | * into several sector-sized transfers. When one sector has |
| 175 | * complete, the IRQ handler will call this function again |
| 176 | * to kick off the transfer of the next sector. |
| 177 | */ |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 178 | alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE); |
| 179 | } |
| 180 | |
| 181 | alcor_write8(priv, ctrl | AU6601_DATA_START_XFER, |
| 182 | AU6601_DATA_XFER_CTRL); |
| 183 | } |
| 184 | |
| 185 | static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read) |
| 186 | { |
| 187 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 188 | size_t blksize, len; |
| 189 | u8 *buf; |
| 190 | |
| 191 | if (!host->blocks) |
| 192 | return; |
| 193 | |
| 194 | if (host->dma_on) { |
| 195 | dev_err(host->dev, "configured DMA but got PIO request.\n"); |
| 196 | return; |
| 197 | } |
| 198 | |
| 199 | if (!!(host->data->flags & MMC_DATA_READ) != read) { |
| 200 | dev_err(host->dev, "got unexpected direction %i != %i\n", |
| 201 | !!(host->data->flags & MMC_DATA_READ), read); |
| 202 | } |
| 203 | |
| 204 | if (!sg_miter_next(&host->sg_miter)) |
| 205 | return; |
| 206 | |
| 207 | blksize = host->data->blksz; |
| 208 | len = min(host->sg_miter.length, blksize); |
| 209 | |
| 210 | dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n", |
| 211 | read ? "read" : "write", blksize); |
| 212 | |
| 213 | host->sg_miter.consumed = len; |
| 214 | host->blocks--; |
| 215 | |
| 216 | buf = host->sg_miter.addr; |
| 217 | |
| 218 | if (read) |
| 219 | ioread32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2); |
| 220 | else |
| 221 | iowrite32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2); |
| 222 | |
| 223 | sg_miter_stop(&host->sg_miter); |
| 224 | } |
| 225 | |
| 226 | static void alcor_prepare_sg_miter(struct alcor_sdmmc_host *host) |
| 227 | { |
| 228 | unsigned int flags = SG_MITER_ATOMIC; |
| 229 | struct mmc_data *data = host->data; |
| 230 | |
| 231 | if (data->flags & MMC_DATA_READ) |
| 232 | flags |= SG_MITER_TO_SG; |
| 233 | else |
| 234 | flags |= SG_MITER_FROM_SG; |
| 235 | sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); |
| 236 | } |
| 237 | |
| 238 | static void alcor_prepare_data(struct alcor_sdmmc_host *host, |
| 239 | struct mmc_command *cmd) |
| 240 | { |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 241 | struct alcor_pci_priv *priv = host->alcor_pci; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 242 | struct mmc_data *data = cmd->data; |
| 243 | |
| 244 | if (!data) |
| 245 | return; |
| 246 | |
| 247 | |
| 248 | host->data = data; |
| 249 | host->data->bytes_xfered = 0; |
| 250 | host->blocks = data->blocks; |
| 251 | host->sg = data->sg; |
| 252 | host->sg_count = data->sg_count; |
| 253 | dev_dbg(host->dev, "prepare DATA: sg %i, blocks: %i\n", |
| 254 | host->sg_count, host->blocks); |
| 255 | |
| 256 | if (data->host_cookie != COOKIE_MAPPED) |
| 257 | alcor_prepare_sg_miter(host); |
| 258 | |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 259 | alcor_write8(priv, 0, AU6601_DATA_XFER_CTRL); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | static void alcor_send_cmd(struct alcor_sdmmc_host *host, |
| 263 | struct mmc_command *cmd, bool set_timeout) |
| 264 | { |
| 265 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 266 | unsigned long timeout = 0; |
| 267 | u8 ctrl = 0; |
| 268 | |
| 269 | host->cmd = cmd; |
| 270 | alcor_prepare_data(host, cmd); |
| 271 | |
| 272 | dev_dbg(host->dev, "send CMD. opcode: 0x%02x, arg; 0x%08x\n", |
| 273 | cmd->opcode, cmd->arg); |
| 274 | alcor_write8(priv, cmd->opcode | 0x40, AU6601_REG_CMD_OPCODE); |
| 275 | alcor_write32be(priv, cmd->arg, AU6601_REG_CMD_ARG); |
| 276 | |
| 277 | switch (mmc_resp_type(cmd)) { |
| 278 | case MMC_RSP_NONE: |
| 279 | ctrl = AU6601_CMD_NO_RESP; |
| 280 | break; |
| 281 | case MMC_RSP_R1: |
| 282 | ctrl = AU6601_CMD_6_BYTE_CRC; |
| 283 | break; |
| 284 | case MMC_RSP_R1B: |
| 285 | ctrl = AU6601_CMD_6_BYTE_CRC | AU6601_CMD_STOP_WAIT_RDY; |
| 286 | break; |
| 287 | case MMC_RSP_R2: |
| 288 | ctrl = AU6601_CMD_17_BYTE_CRC; |
| 289 | break; |
| 290 | case MMC_RSP_R3: |
| 291 | ctrl = AU6601_CMD_6_BYTE_WO_CRC; |
| 292 | break; |
| 293 | default: |
| 294 | dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n", |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 295 | mmc_hostname(mmc_from_priv(host)), mmc_resp_type(cmd)); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 296 | break; |
| 297 | } |
| 298 | |
| 299 | if (set_timeout) { |
| 300 | if (!cmd->data && cmd->busy_timeout) |
| 301 | timeout = cmd->busy_timeout; |
| 302 | else |
| 303 | timeout = 10000; |
| 304 | |
| 305 | schedule_delayed_work(&host->timeout_work, |
| 306 | msecs_to_jiffies(timeout)); |
| 307 | } |
| 308 | |
| 309 | dev_dbg(host->dev, "xfer ctrl: 0x%02x; timeout: %lu\n", ctrl, timeout); |
| 310 | alcor_write8(priv, ctrl | AU6601_CMD_START_XFER, |
| 311 | AU6601_CMD_XFER_CTRL); |
| 312 | } |
| 313 | |
| 314 | static void alcor_request_complete(struct alcor_sdmmc_host *host, |
| 315 | bool cancel_timeout) |
| 316 | { |
| 317 | struct mmc_request *mrq; |
| 318 | |
| 319 | /* |
| 320 | * If this work gets rescheduled while running, it will |
| 321 | * be run again afterwards but without any active request. |
| 322 | */ |
| 323 | if (!host->mrq) |
| 324 | return; |
| 325 | |
| 326 | if (cancel_timeout) |
| 327 | cancel_delayed_work(&host->timeout_work); |
| 328 | |
| 329 | mrq = host->mrq; |
| 330 | |
| 331 | host->mrq = NULL; |
| 332 | host->cmd = NULL; |
| 333 | host->data = NULL; |
| 334 | host->dma_on = 0; |
| 335 | |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 336 | mmc_request_done(mmc_from_priv(host), mrq); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | static void alcor_finish_data(struct alcor_sdmmc_host *host) |
| 340 | { |
| 341 | struct mmc_data *data; |
| 342 | |
| 343 | data = host->data; |
| 344 | host->data = NULL; |
| 345 | host->dma_on = 0; |
| 346 | |
| 347 | /* |
| 348 | * The specification states that the block count register must |
| 349 | * be updated, but it does not specify at what point in the |
| 350 | * data flow. That makes the register entirely useless to read |
| 351 | * back so we have to assume that nothing made it to the card |
| 352 | * in the event of an error. |
| 353 | */ |
| 354 | if (data->error) |
| 355 | data->bytes_xfered = 0; |
| 356 | else |
| 357 | data->bytes_xfered = data->blksz * data->blocks; |
| 358 | |
| 359 | /* |
| 360 | * Need to send CMD12 if - |
| 361 | * a) open-ended multiblock transfer (no CMD23) |
| 362 | * b) error in multiblock transfer |
| 363 | */ |
| 364 | if (data->stop && |
| 365 | (data->error || |
| 366 | !host->mrq->sbc)) { |
| 367 | |
| 368 | /* |
| 369 | * The controller needs a reset of internal state machines |
| 370 | * upon error conditions. |
| 371 | */ |
| 372 | if (data->error) |
| 373 | alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); |
| 374 | |
| 375 | alcor_unmask_sd_irqs(host); |
| 376 | alcor_send_cmd(host, data->stop, false); |
| 377 | return; |
| 378 | } |
| 379 | |
| 380 | alcor_request_complete(host, 1); |
| 381 | } |
| 382 | |
| 383 | static void alcor_err_irq(struct alcor_sdmmc_host *host, u32 intmask) |
| 384 | { |
| 385 | dev_dbg(host->dev, "ERR IRQ %x\n", intmask); |
| 386 | |
| 387 | if (host->cmd) { |
| 388 | if (intmask & AU6601_INT_CMD_TIMEOUT_ERR) |
| 389 | host->cmd->error = -ETIMEDOUT; |
| 390 | else |
| 391 | host->cmd->error = -EILSEQ; |
| 392 | } |
| 393 | |
| 394 | if (host->data) { |
| 395 | if (intmask & AU6601_INT_DATA_TIMEOUT_ERR) |
| 396 | host->data->error = -ETIMEDOUT; |
| 397 | else |
| 398 | host->data->error = -EILSEQ; |
| 399 | |
| 400 | host->data->bytes_xfered = 0; |
| 401 | } |
| 402 | |
| 403 | alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); |
| 404 | alcor_request_complete(host, 1); |
| 405 | } |
| 406 | |
| 407 | static int alcor_cmd_irq_done(struct alcor_sdmmc_host *host, u32 intmask) |
| 408 | { |
| 409 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 410 | |
| 411 | intmask &= AU6601_INT_CMD_END; |
| 412 | |
| 413 | if (!intmask) |
| 414 | return true; |
| 415 | |
| 416 | /* got CMD_END but no CMD is in progress, wake thread an process the |
| 417 | * error |
| 418 | */ |
| 419 | if (!host->cmd) |
| 420 | return false; |
| 421 | |
| 422 | if (host->cmd->flags & MMC_RSP_PRESENT) { |
| 423 | struct mmc_command *cmd = host->cmd; |
| 424 | |
| 425 | cmd->resp[0] = alcor_read32be(priv, AU6601_REG_CMD_RSP0); |
| 426 | dev_dbg(host->dev, "RSP0: 0x%04x\n", cmd->resp[0]); |
| 427 | if (host->cmd->flags & MMC_RSP_136) { |
| 428 | cmd->resp[1] = |
| 429 | alcor_read32be(priv, AU6601_REG_CMD_RSP1); |
| 430 | cmd->resp[2] = |
| 431 | alcor_read32be(priv, AU6601_REG_CMD_RSP2); |
| 432 | cmd->resp[3] = |
| 433 | alcor_read32be(priv, AU6601_REG_CMD_RSP3); |
| 434 | dev_dbg(host->dev, "RSP1,2,3: 0x%04x 0x%04x 0x%04x\n", |
| 435 | cmd->resp[1], cmd->resp[2], cmd->resp[3]); |
| 436 | } |
| 437 | |
| 438 | } |
| 439 | |
| 440 | host->cmd->error = 0; |
| 441 | |
| 442 | /* Processed actual command. */ |
| 443 | if (!host->data) |
| 444 | return false; |
| 445 | |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 446 | alcor_trigger_data_transfer(host); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 447 | host->cmd = NULL; |
| 448 | return true; |
| 449 | } |
| 450 | |
| 451 | static void alcor_cmd_irq_thread(struct alcor_sdmmc_host *host, u32 intmask) |
| 452 | { |
| 453 | intmask &= AU6601_INT_CMD_END; |
| 454 | |
| 455 | if (!intmask) |
| 456 | return; |
| 457 | |
| 458 | if (!host->cmd && intmask & AU6601_INT_CMD_END) { |
| 459 | dev_dbg(host->dev, "Got command interrupt 0x%08x even though no command operation was in progress.\n", |
| 460 | intmask); |
| 461 | } |
| 462 | |
| 463 | /* Processed actual command. */ |
| 464 | if (!host->data) |
| 465 | alcor_request_complete(host, 1); |
| 466 | else |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 467 | alcor_trigger_data_transfer(host); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 468 | host->cmd = NULL; |
| 469 | } |
| 470 | |
| 471 | static int alcor_data_irq_done(struct alcor_sdmmc_host *host, u32 intmask) |
| 472 | { |
| 473 | u32 tmp; |
| 474 | |
| 475 | intmask &= AU6601_INT_DATA_MASK; |
| 476 | |
| 477 | /* nothing here to do */ |
| 478 | if (!intmask) |
| 479 | return 1; |
| 480 | |
| 481 | /* we was too fast and got DATA_END after it was processed? |
| 482 | * lets ignore it for now. |
| 483 | */ |
| 484 | if (!host->data && intmask == AU6601_INT_DATA_END) |
| 485 | return 1; |
| 486 | |
| 487 | /* looks like an error, so lets handle it. */ |
| 488 | if (!host->data) |
| 489 | return 0; |
| 490 | |
| 491 | tmp = intmask & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY |
| 492 | | AU6601_INT_DMA_END); |
| 493 | switch (tmp) { |
| 494 | case 0: |
| 495 | break; |
| 496 | case AU6601_INT_READ_BUF_RDY: |
| 497 | alcor_trf_block_pio(host, true); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 498 | return 1; |
| 499 | case AU6601_INT_WRITE_BUF_RDY: |
| 500 | alcor_trf_block_pio(host, false); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 501 | return 1; |
| 502 | case AU6601_INT_DMA_END: |
| 503 | if (!host->sg_count) |
| 504 | break; |
| 505 | |
| 506 | alcor_data_set_dma(host); |
| 507 | break; |
| 508 | default: |
| 509 | dev_err(host->dev, "Got READ_BUF_RDY and WRITE_BUF_RDY at same time\n"); |
| 510 | break; |
| 511 | } |
| 512 | |
Daniel Drake | 157c99c | 2019-03-26 15:04:14 +0800 | [diff] [blame] | 513 | if (intmask & AU6601_INT_DATA_END) { |
| 514 | if (!host->dma_on && host->blocks) { |
| 515 | alcor_trigger_data_transfer(host); |
| 516 | return 1; |
| 517 | } else { |
| 518 | return 0; |
| 519 | } |
| 520 | } |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 521 | |
| 522 | return 1; |
| 523 | } |
| 524 | |
| 525 | static void alcor_data_irq_thread(struct alcor_sdmmc_host *host, u32 intmask) |
| 526 | { |
| 527 | intmask &= AU6601_INT_DATA_MASK; |
| 528 | |
| 529 | if (!intmask) |
| 530 | return; |
| 531 | |
| 532 | if (!host->data) { |
| 533 | dev_dbg(host->dev, "Got data interrupt 0x%08x even though no data operation was in progress.\n", |
| 534 | intmask); |
| 535 | alcor_reset(host, AU6601_RESET_DATA); |
| 536 | return; |
| 537 | } |
| 538 | |
| 539 | if (alcor_data_irq_done(host, intmask)) |
| 540 | return; |
| 541 | |
| 542 | if ((intmask & AU6601_INT_DATA_END) || !host->blocks || |
| 543 | (host->dma_on && !host->sg_count)) |
| 544 | alcor_finish_data(host); |
| 545 | } |
| 546 | |
| 547 | static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask) |
| 548 | { |
| 549 | dev_dbg(host->dev, "card %s\n", |
| 550 | intmask & AU6601_INT_CARD_REMOVE ? "removed" : "inserted"); |
| 551 | |
| 552 | if (host->mrq) { |
| 553 | dev_dbg(host->dev, "cancel all pending tasks.\n"); |
| 554 | |
| 555 | if (host->data) |
| 556 | host->data->error = -ENOMEDIUM; |
| 557 | |
| 558 | if (host->cmd) |
| 559 | host->cmd->error = -ENOMEDIUM; |
| 560 | else |
| 561 | host->mrq->cmd->error = -ENOMEDIUM; |
| 562 | |
| 563 | alcor_request_complete(host, 1); |
| 564 | } |
| 565 | |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 566 | mmc_detect_change(mmc_from_priv(host), msecs_to_jiffies(1)); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | static irqreturn_t alcor_irq_thread(int irq, void *d) |
| 570 | { |
| 571 | struct alcor_sdmmc_host *host = d; |
| 572 | irqreturn_t ret = IRQ_HANDLED; |
| 573 | u32 intmask, tmp; |
| 574 | |
| 575 | mutex_lock(&host->cmd_mutex); |
| 576 | |
| 577 | intmask = host->irq_status_sd; |
| 578 | |
| 579 | /* some thing bad */ |
| 580 | if (unlikely(!intmask || AU6601_INT_ALL_MASK == intmask)) { |
| 581 | dev_dbg(host->dev, "unexpected IRQ: 0x%04x\n", intmask); |
| 582 | ret = IRQ_NONE; |
| 583 | goto exit; |
| 584 | } |
| 585 | |
| 586 | tmp = intmask & (AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK); |
| 587 | if (tmp) { |
| 588 | if (tmp & AU6601_INT_ERROR_MASK) |
| 589 | alcor_err_irq(host, tmp); |
| 590 | else { |
| 591 | alcor_cmd_irq_thread(host, tmp); |
| 592 | alcor_data_irq_thread(host, tmp); |
| 593 | } |
| 594 | intmask &= ~(AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK); |
| 595 | } |
| 596 | |
| 597 | if (intmask & (AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE)) { |
| 598 | alcor_cd_irq(host, intmask); |
| 599 | intmask &= ~(AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE); |
| 600 | } |
| 601 | |
| 602 | if (intmask & AU6601_INT_OVER_CURRENT_ERR) { |
| 603 | dev_warn(host->dev, |
| 604 | "warning: over current detected!\n"); |
| 605 | intmask &= ~AU6601_INT_OVER_CURRENT_ERR; |
| 606 | } |
| 607 | |
| 608 | if (intmask) |
| 609 | dev_dbg(host->dev, "got not handled IRQ: 0x%04x\n", intmask); |
| 610 | |
| 611 | exit: |
| 612 | mutex_unlock(&host->cmd_mutex); |
| 613 | alcor_unmask_sd_irqs(host); |
| 614 | return ret; |
| 615 | } |
| 616 | |
| 617 | |
| 618 | static irqreturn_t alcor_irq(int irq, void *d) |
| 619 | { |
| 620 | struct alcor_sdmmc_host *host = d; |
| 621 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 622 | u32 status, tmp; |
| 623 | irqreturn_t ret; |
| 624 | int cmd_done, data_done; |
| 625 | |
| 626 | status = alcor_read32(priv, AU6601_REG_INT_STATUS); |
| 627 | if (!status) |
| 628 | return IRQ_NONE; |
| 629 | |
| 630 | alcor_write32(priv, status, AU6601_REG_INT_STATUS); |
| 631 | |
| 632 | tmp = status & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY |
| 633 | | AU6601_INT_DATA_END | AU6601_INT_DMA_END |
| 634 | | AU6601_INT_CMD_END); |
| 635 | if (tmp == status) { |
| 636 | cmd_done = alcor_cmd_irq_done(host, tmp); |
| 637 | data_done = alcor_data_irq_done(host, tmp); |
| 638 | /* use fast path for simple tasks */ |
| 639 | if (cmd_done && data_done) { |
| 640 | ret = IRQ_HANDLED; |
| 641 | goto alcor_irq_done; |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | host->irq_status_sd = status; |
| 646 | ret = IRQ_WAKE_THREAD; |
| 647 | alcor_mask_sd_irqs(host); |
| 648 | alcor_irq_done: |
| 649 | return ret; |
| 650 | } |
| 651 | |
| 652 | static void alcor_set_clock(struct alcor_sdmmc_host *host, unsigned int clock) |
| 653 | { |
| 654 | struct alcor_pci_priv *priv = host->alcor_pci; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 655 | int i, diff = 0x7fffffff, tmp_clock = 0; |
| 656 | u16 clk_src = 0; |
| 657 | u8 clk_div = 0; |
| 658 | |
| 659 | if (clock == 0) { |
| 660 | alcor_write16(priv, 0, AU6601_CLK_SELECT); |
| 661 | return; |
| 662 | } |
| 663 | |
| 664 | for (i = 0; i < ARRAY_SIZE(alcor_pll_cfg); i++) { |
| 665 | unsigned int tmp_div, tmp_diff; |
| 666 | const struct alcor_pll_conf *cfg = &alcor_pll_cfg[i]; |
| 667 | |
| 668 | tmp_div = DIV_ROUND_UP(cfg->clk_src_freq, clock); |
| 669 | if (cfg->min_div > tmp_div || tmp_div > cfg->max_div) |
| 670 | continue; |
| 671 | |
| 672 | tmp_clock = DIV_ROUND_UP(cfg->clk_src_freq, tmp_div); |
| 673 | tmp_diff = abs(clock - tmp_clock); |
| 674 | |
Colin Ian King | 42248a9 | 2019-07-01 18:52:46 +0100 | [diff] [blame] | 675 | if (tmp_diff < diff) { |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 676 | diff = tmp_diff; |
| 677 | clk_src = cfg->clk_src_reg; |
| 678 | clk_div = tmp_div; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 679 | } |
| 680 | } |
| 681 | |
| 682 | clk_src |= ((clk_div - 1) << 8); |
| 683 | clk_src |= AU6601_CLK_ENABLE; |
| 684 | |
| 685 | dev_dbg(host->dev, "set freq %d cal freq %d, use div %d, mod %x\n", |
| 686 | clock, tmp_clock, clk_div, clk_src); |
| 687 | |
| 688 | alcor_write16(priv, clk_src, AU6601_CLK_SELECT); |
| 689 | |
| 690 | } |
| 691 | |
| 692 | static void alcor_set_timing(struct mmc_host *mmc, struct mmc_ios *ios) |
| 693 | { |
| 694 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 695 | |
| 696 | if (ios->timing == MMC_TIMING_LEGACY) { |
| 697 | alcor_rmw8(host, AU6601_CLK_DELAY, |
| 698 | AU6601_CLK_POSITIVE_EDGE_ALL, 0); |
| 699 | } else { |
| 700 | alcor_rmw8(host, AU6601_CLK_DELAY, |
| 701 | 0, AU6601_CLK_POSITIVE_EDGE_ALL); |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | static void alcor_set_bus_width(struct mmc_host *mmc, struct mmc_ios *ios) |
| 706 | { |
| 707 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 708 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 709 | |
| 710 | if (ios->bus_width == MMC_BUS_WIDTH_1) { |
| 711 | alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); |
| 712 | } else if (ios->bus_width == MMC_BUS_WIDTH_4) { |
| 713 | alcor_write8(priv, AU6601_BUS_WIDTH_4BIT, |
| 714 | AU6601_REG_BUS_CTRL); |
| 715 | } else |
| 716 | dev_err(host->dev, "Unknown BUS mode\n"); |
| 717 | |
| 718 | } |
| 719 | |
| 720 | static int alcor_card_busy(struct mmc_host *mmc) |
| 721 | { |
| 722 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 723 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 724 | u8 status; |
| 725 | |
| 726 | /* Check whether dat[0:3] low */ |
| 727 | status = alcor_read8(priv, AU6601_DATA_PIN_STATE); |
| 728 | |
| 729 | return !(status & AU6601_BUS_STAT_DAT_MASK); |
| 730 | } |
| 731 | |
| 732 | static int alcor_get_cd(struct mmc_host *mmc) |
| 733 | { |
| 734 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 735 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 736 | u8 detect; |
| 737 | |
| 738 | detect = alcor_read8(priv, AU6601_DETECT_STATUS) |
| 739 | & AU6601_DETECT_STATUS_M; |
| 740 | /* check if card is present then send command and data */ |
| 741 | return (detect == AU6601_SD_DETECTED); |
| 742 | } |
| 743 | |
| 744 | static int alcor_get_ro(struct mmc_host *mmc) |
| 745 | { |
| 746 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 747 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 748 | u8 status; |
| 749 | |
| 750 | /* get write protect pin status */ |
| 751 | status = alcor_read8(priv, AU6601_INTERFACE_MODE_CTRL); |
| 752 | |
| 753 | return !!(status & AU6601_SD_CARD_WP); |
| 754 | } |
| 755 | |
| 756 | static void alcor_request(struct mmc_host *mmc, struct mmc_request *mrq) |
| 757 | { |
| 758 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 759 | |
| 760 | mutex_lock(&host->cmd_mutex); |
| 761 | |
| 762 | host->mrq = mrq; |
| 763 | |
| 764 | /* check if card is present then send command and data */ |
| 765 | if (alcor_get_cd(mmc)) |
| 766 | alcor_send_cmd(host, mrq->cmd, true); |
| 767 | else { |
| 768 | mrq->cmd->error = -ENOMEDIUM; |
| 769 | alcor_request_complete(host, 1); |
| 770 | } |
| 771 | |
| 772 | mutex_unlock(&host->cmd_mutex); |
| 773 | } |
| 774 | |
| 775 | static void alcor_pre_req(struct mmc_host *mmc, |
| 776 | struct mmc_request *mrq) |
| 777 | { |
| 778 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 779 | struct mmc_data *data = mrq->data; |
| 780 | struct mmc_command *cmd = mrq->cmd; |
| 781 | struct scatterlist *sg; |
| 782 | unsigned int i, sg_len; |
| 783 | |
| 784 | if (!data || !cmd) |
| 785 | return; |
| 786 | |
| 787 | data->host_cookie = COOKIE_UNMAPPED; |
| 788 | |
| 789 | /* FIXME: looks like the DMA engine works only with CMD18 */ |
Daniel Drake | e5a34b0 | 2019-03-26 15:04:15 +0800 | [diff] [blame] | 790 | if (cmd->opcode != MMC_READ_MULTIPLE_BLOCK |
| 791 | && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 792 | return; |
| 793 | /* |
| 794 | * We don't do DMA on "complex" transfers, i.e. with |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 795 | * non-word-aligned buffers or lengths. A future improvement |
| 796 | * could be made to use temporary DMA bounce-buffers when these |
| 797 | * requirements are not met. |
| 798 | * |
| 799 | * Also, we don't bother with all the DMA setup overhead for |
| 800 | * short transfers. |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 801 | */ |
| 802 | if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE) |
| 803 | return; |
| 804 | |
| 805 | if (data->blksz & 3) |
| 806 | return; |
| 807 | |
| 808 | for_each_sg(data->sg, sg, data->sg_len, i) { |
| 809 | if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE) |
| 810 | return; |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 811 | if (sg->offset != 0) |
| 812 | return; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 813 | } |
| 814 | |
| 815 | /* This data might be unmapped at this time */ |
| 816 | |
| 817 | sg_len = dma_map_sg(host->dev, data->sg, data->sg_len, |
| 818 | mmc_get_dma_dir(data)); |
| 819 | if (sg_len) |
| 820 | data->host_cookie = COOKIE_MAPPED; |
| 821 | |
| 822 | data->sg_count = sg_len; |
| 823 | } |
| 824 | |
| 825 | static void alcor_post_req(struct mmc_host *mmc, |
| 826 | struct mmc_request *mrq, |
| 827 | int err) |
| 828 | { |
| 829 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 830 | struct mmc_data *data = mrq->data; |
| 831 | |
| 832 | if (!data) |
| 833 | return; |
| 834 | |
| 835 | if (data->host_cookie == COOKIE_MAPPED) { |
| 836 | dma_unmap_sg(host->dev, |
| 837 | data->sg, |
| 838 | data->sg_len, |
| 839 | mmc_get_dma_dir(data)); |
| 840 | } |
| 841 | |
| 842 | data->host_cookie = COOKIE_UNMAPPED; |
| 843 | } |
| 844 | |
| 845 | static void alcor_set_power_mode(struct mmc_host *mmc, struct mmc_ios *ios) |
| 846 | { |
| 847 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 848 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 849 | |
| 850 | switch (ios->power_mode) { |
| 851 | case MMC_POWER_OFF: |
| 852 | alcor_set_clock(host, ios->clock); |
| 853 | /* set all pins to input */ |
| 854 | alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); |
| 855 | /* turn of VDD */ |
| 856 | alcor_write8(priv, 0, AU6601_POWER_CONTROL); |
| 857 | break; |
| 858 | case MMC_POWER_UP: |
| 859 | break; |
| 860 | case MMC_POWER_ON: |
| 861 | /* This is most trickiest part. The order and timings of |
| 862 | * instructions seems to play important role. Any changes may |
| 863 | * confuse internal state engine if this HW. |
| 864 | * FIXME: If we will ever get access to documentation, then this |
| 865 | * part should be reviewed again. |
| 866 | */ |
| 867 | |
| 868 | /* enable SD card mode */ |
| 869 | alcor_write8(priv, AU6601_SD_CARD, |
| 870 | AU6601_ACTIVE_CTRL); |
| 871 | /* set signal voltage to 3.3V */ |
| 872 | alcor_write8(priv, 0, AU6601_OPT); |
| 873 | /* no documentation about clk delay, for now just try to mimic |
| 874 | * original driver. |
| 875 | */ |
| 876 | alcor_write8(priv, 0x20, AU6601_CLK_DELAY); |
| 877 | /* set BUS width to 1 bit */ |
| 878 | alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); |
| 879 | /* set CLK first time */ |
| 880 | alcor_set_clock(host, ios->clock); |
| 881 | /* power on VDD */ |
| 882 | alcor_write8(priv, AU6601_SD_CARD, |
| 883 | AU6601_POWER_CONTROL); |
| 884 | /* wait until the CLK will get stable */ |
| 885 | mdelay(20); |
| 886 | /* set CLK again, mimic original driver. */ |
| 887 | alcor_set_clock(host, ios->clock); |
| 888 | |
| 889 | /* enable output */ |
| 890 | alcor_write8(priv, AU6601_SD_CARD, |
| 891 | AU6601_OUTPUT_ENABLE); |
| 892 | /* The clk will not work on au6621. We need to trigger data |
| 893 | * transfer. |
| 894 | */ |
| 895 | alcor_write8(priv, AU6601_DATA_WRITE, |
| 896 | AU6601_DATA_XFER_CTRL); |
| 897 | /* configure timeout. Not clear what exactly it means. */ |
| 898 | alcor_write8(priv, 0x7d, AU6601_TIME_OUT_CTRL); |
| 899 | mdelay(100); |
| 900 | break; |
| 901 | default: |
| 902 | dev_err(host->dev, "Unknown power parameter\n"); |
| 903 | } |
| 904 | } |
| 905 | |
| 906 | static void alcor_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
| 907 | { |
| 908 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 909 | |
| 910 | mutex_lock(&host->cmd_mutex); |
| 911 | |
| 912 | dev_dbg(host->dev, "set ios. bus width: %x, power mode: %x\n", |
| 913 | ios->bus_width, ios->power_mode); |
| 914 | |
| 915 | if (ios->power_mode != host->cur_power_mode) { |
| 916 | alcor_set_power_mode(mmc, ios); |
| 917 | host->cur_power_mode = ios->power_mode; |
| 918 | } else { |
| 919 | alcor_set_timing(mmc, ios); |
| 920 | alcor_set_bus_width(mmc, ios); |
| 921 | alcor_set_clock(host, ios->clock); |
| 922 | } |
| 923 | |
| 924 | mutex_unlock(&host->cmd_mutex); |
| 925 | } |
| 926 | |
| 927 | static int alcor_signal_voltage_switch(struct mmc_host *mmc, |
| 928 | struct mmc_ios *ios) |
| 929 | { |
| 930 | struct alcor_sdmmc_host *host = mmc_priv(mmc); |
| 931 | |
| 932 | mutex_lock(&host->cmd_mutex); |
| 933 | |
| 934 | switch (ios->signal_voltage) { |
| 935 | case MMC_SIGNAL_VOLTAGE_330: |
| 936 | alcor_rmw8(host, AU6601_OPT, AU6601_OPT_SD_18V, 0); |
| 937 | break; |
| 938 | case MMC_SIGNAL_VOLTAGE_180: |
| 939 | alcor_rmw8(host, AU6601_OPT, 0, AU6601_OPT_SD_18V); |
| 940 | break; |
| 941 | default: |
| 942 | /* No signal voltage switch required */ |
| 943 | break; |
| 944 | } |
| 945 | |
| 946 | mutex_unlock(&host->cmd_mutex); |
| 947 | return 0; |
| 948 | } |
| 949 | |
| 950 | static const struct mmc_host_ops alcor_sdc_ops = { |
| 951 | .card_busy = alcor_card_busy, |
| 952 | .get_cd = alcor_get_cd, |
| 953 | .get_ro = alcor_get_ro, |
| 954 | .post_req = alcor_post_req, |
| 955 | .pre_req = alcor_pre_req, |
| 956 | .request = alcor_request, |
| 957 | .set_ios = alcor_set_ios, |
| 958 | .start_signal_voltage_switch = alcor_signal_voltage_switch, |
| 959 | }; |
| 960 | |
| 961 | static void alcor_timeout_timer(struct work_struct *work) |
| 962 | { |
| 963 | struct delayed_work *d = to_delayed_work(work); |
| 964 | struct alcor_sdmmc_host *host = container_of(d, struct alcor_sdmmc_host, |
| 965 | timeout_work); |
| 966 | mutex_lock(&host->cmd_mutex); |
| 967 | |
| 968 | dev_dbg(host->dev, "triggered timeout\n"); |
| 969 | if (host->mrq) { |
| 970 | dev_err(host->dev, "Timeout waiting for hardware interrupt.\n"); |
| 971 | |
| 972 | if (host->data) { |
| 973 | host->data->error = -ETIMEDOUT; |
| 974 | } else { |
| 975 | if (host->cmd) |
| 976 | host->cmd->error = -ETIMEDOUT; |
| 977 | else |
| 978 | host->mrq->cmd->error = -ETIMEDOUT; |
| 979 | } |
| 980 | |
| 981 | alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); |
| 982 | alcor_request_complete(host, 0); |
| 983 | } |
| 984 | |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 985 | mutex_unlock(&host->cmd_mutex); |
| 986 | } |
| 987 | |
| 988 | static void alcor_hw_init(struct alcor_sdmmc_host *host) |
| 989 | { |
| 990 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 991 | struct alcor_dev_cfg *cfg = priv->cfg; |
| 992 | |
| 993 | /* FIXME: This part is a mimics HW init of original driver. |
| 994 | * If we will ever get access to documentation, then this part |
| 995 | * should be reviewed again. |
| 996 | */ |
| 997 | |
| 998 | /* reset command state engine */ |
| 999 | alcor_reset(host, AU6601_RESET_CMD); |
| 1000 | |
| 1001 | alcor_write8(priv, 0, AU6601_DMA_BOUNDARY); |
| 1002 | /* enable sd card mode */ |
| 1003 | alcor_write8(priv, AU6601_SD_CARD, AU6601_ACTIVE_CTRL); |
| 1004 | |
| 1005 | /* set BUS width to 1 bit */ |
| 1006 | alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); |
| 1007 | |
| 1008 | /* reset data state engine */ |
| 1009 | alcor_reset(host, AU6601_RESET_DATA); |
| 1010 | /* Not sure if a voodoo with AU6601_DMA_BOUNDARY is really needed */ |
| 1011 | alcor_write8(priv, 0, AU6601_DMA_BOUNDARY); |
| 1012 | |
| 1013 | alcor_write8(priv, 0, AU6601_INTERFACE_MODE_CTRL); |
| 1014 | /* not clear what we are doing here. */ |
| 1015 | alcor_write8(priv, 0x44, AU6601_PAD_DRIVE0); |
| 1016 | alcor_write8(priv, 0x44, AU6601_PAD_DRIVE1); |
| 1017 | alcor_write8(priv, 0x00, AU6601_PAD_DRIVE2); |
| 1018 | |
| 1019 | /* for 6601 - dma_boundary; for 6621 - dma_page_cnt |
| 1020 | * exact meaning of this register is not clear. |
| 1021 | */ |
| 1022 | alcor_write8(priv, cfg->dma, AU6601_DMA_BOUNDARY); |
| 1023 | |
| 1024 | /* make sure all pins are set to input and VDD is off */ |
| 1025 | alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); |
| 1026 | alcor_write8(priv, 0, AU6601_POWER_CONTROL); |
| 1027 | |
| 1028 | alcor_write8(priv, AU6601_DETECT_EN, AU6601_DETECT_STATUS); |
| 1029 | /* now we should be safe to enable IRQs */ |
| 1030 | alcor_unmask_sd_irqs(host); |
| 1031 | } |
| 1032 | |
| 1033 | static void alcor_hw_uninit(struct alcor_sdmmc_host *host) |
| 1034 | { |
| 1035 | struct alcor_pci_priv *priv = host->alcor_pci; |
| 1036 | |
| 1037 | alcor_mask_sd_irqs(host); |
| 1038 | alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); |
| 1039 | |
| 1040 | alcor_write8(priv, 0, AU6601_DETECT_STATUS); |
| 1041 | |
| 1042 | alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); |
| 1043 | alcor_write8(priv, 0, AU6601_POWER_CONTROL); |
| 1044 | |
| 1045 | alcor_write8(priv, 0, AU6601_OPT); |
| 1046 | } |
| 1047 | |
| 1048 | static void alcor_init_mmc(struct alcor_sdmmc_host *host) |
| 1049 | { |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 1050 | struct mmc_host *mmc = mmc_from_priv(host); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1051 | |
| 1052 | mmc->f_min = AU6601_MIN_CLOCK; |
| 1053 | mmc->f_max = AU6601_MAX_CLOCK; |
| 1054 | mmc->ocr_avail = MMC_VDD_33_34; |
| 1055 | mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
| 1056 | | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
| 1057 | | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50; |
| 1058 | mmc->caps2 = MMC_CAP2_NO_SDIO; |
| 1059 | mmc->ops = &alcor_sdc_ops; |
| 1060 | |
Daniel Drake | 5ea4769 | 2019-03-20 14:36:53 +0800 | [diff] [blame] | 1061 | /* The hardware does DMA data transfer of 4096 bytes to/from a single |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 1062 | * buffer address. Scatterlists are not supported at the hardware |
| 1063 | * level, however we can work with them at the driver level, |
| 1064 | * provided that each segment is exactly 4096 bytes in size. |
| 1065 | * Upon DMA completion of a single segment (signalled via IRQ), we |
| 1066 | * immediately proceed to transfer the next segment from the |
| 1067 | * scatterlist. |
Daniel Drake | 5ea4769 | 2019-03-20 14:36:53 +0800 | [diff] [blame] | 1068 | * |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 1069 | * The overall request is limited to 240 sectors, matching the |
| 1070 | * original vendor driver. |
Daniel Drake | 5ea4769 | 2019-03-20 14:36:53 +0800 | [diff] [blame] | 1071 | */ |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1072 | mmc->max_segs = AU6601_MAX_DMA_SEGMENTS; |
| 1073 | mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE; |
Daniel Drake | c671b6d | 2019-04-29 13:14:26 +0800 | [diff] [blame] | 1074 | mmc->max_blk_count = 240; |
| 1075 | mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; |
| 1076 | dma_set_max_seg_size(host->dev, mmc->max_seg_size); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1077 | } |
| 1078 | |
| 1079 | static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) |
| 1080 | { |
| 1081 | struct alcor_pci_priv *priv = pdev->dev.platform_data; |
| 1082 | struct mmc_host *mmc; |
| 1083 | struct alcor_sdmmc_host *host; |
| 1084 | int ret; |
| 1085 | |
| 1086 | mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); |
| 1087 | if (!mmc) { |
| 1088 | dev_err(&pdev->dev, "Can't allocate MMC\n"); |
| 1089 | return -ENOMEM; |
| 1090 | } |
| 1091 | |
| 1092 | host = mmc_priv(mmc); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1093 | host->dev = &pdev->dev; |
| 1094 | host->cur_power_mode = MMC_POWER_UNDEFINED; |
| 1095 | host->alcor_pci = priv; |
| 1096 | |
| 1097 | /* make sure irqs are disabled */ |
| 1098 | alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); |
| 1099 | alcor_write32(priv, 0, AU6601_MS_INT_ENABLE); |
| 1100 | |
| 1101 | ret = devm_request_threaded_irq(&pdev->dev, priv->irq, |
| 1102 | alcor_irq, alcor_irq_thread, IRQF_SHARED, |
| 1103 | DRV_NAME_ALCOR_PCI_SDMMC, host); |
| 1104 | |
| 1105 | if (ret) { |
| 1106 | dev_err(&pdev->dev, "Failed to get irq for data line\n"); |
Christophe JAILLET | 7c277dd | 2020-04-26 22:23:55 +0200 | [diff] [blame] | 1107 | goto free_host; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1108 | } |
| 1109 | |
| 1110 | mutex_init(&host->cmd_mutex); |
| 1111 | INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer); |
| 1112 | |
| 1113 | alcor_init_mmc(host); |
| 1114 | alcor_hw_init(host); |
| 1115 | |
| 1116 | dev_set_drvdata(&pdev->dev, host); |
| 1117 | mmc_add_host(mmc); |
| 1118 | return 0; |
Christophe JAILLET | 7c277dd | 2020-04-26 22:23:55 +0200 | [diff] [blame] | 1119 | |
| 1120 | free_host: |
| 1121 | mmc_free_host(mmc); |
| 1122 | return ret; |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1123 | } |
| 1124 | |
| 1125 | static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) |
| 1126 | { |
| 1127 | struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev); |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 1128 | struct mmc_host *mmc = mmc_from_priv(host); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1129 | |
| 1130 | if (cancel_delayed_work_sync(&host->timeout_work)) |
| 1131 | alcor_request_complete(host, 0); |
| 1132 | |
| 1133 | alcor_hw_uninit(host); |
Kamlesh Gurudasani | c081e7f | 2019-05-02 11:28:16 +0530 | [diff] [blame] | 1134 | mmc_remove_host(mmc); |
| 1135 | mmc_free_host(mmc); |
Oleksij Rempel | c5413ad | 2018-12-02 11:30:46 +0100 | [diff] [blame] | 1136 | |
| 1137 | return 0; |
| 1138 | } |
| 1139 | |
| 1140 | #ifdef CONFIG_PM_SLEEP |
| 1141 | static int alcor_pci_sdmmc_suspend(struct device *dev) |
| 1142 | { |
| 1143 | struct alcor_sdmmc_host *host = dev_get_drvdata(dev); |
| 1144 | |
| 1145 | if (cancel_delayed_work_sync(&host->timeout_work)) |
| 1146 | alcor_request_complete(host, 0); |
| 1147 | |
| 1148 | alcor_hw_uninit(host); |
| 1149 | |
| 1150 | return 0; |
| 1151 | } |
| 1152 | |
| 1153 | static int alcor_pci_sdmmc_resume(struct device *dev) |
| 1154 | { |
| 1155 | struct alcor_sdmmc_host *host = dev_get_drvdata(dev); |
| 1156 | |
| 1157 | alcor_hw_init(host); |
| 1158 | |
| 1159 | return 0; |
| 1160 | } |
| 1161 | #endif /* CONFIG_PM_SLEEP */ |
| 1162 | |
| 1163 | static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend, |
| 1164 | alcor_pci_sdmmc_resume); |
| 1165 | |
| 1166 | static const struct platform_device_id alcor_pci_sdmmc_ids[] = { |
| 1167 | { |
| 1168 | .name = DRV_NAME_ALCOR_PCI_SDMMC, |
| 1169 | }, { |
| 1170 | /* sentinel */ |
| 1171 | } |
| 1172 | }; |
| 1173 | MODULE_DEVICE_TABLE(platform, alcor_pci_sdmmc_ids); |
| 1174 | |
| 1175 | static struct platform_driver alcor_pci_sdmmc_driver = { |
| 1176 | .probe = alcor_pci_sdmmc_drv_probe, |
| 1177 | .remove = alcor_pci_sdmmc_drv_remove, |
| 1178 | .id_table = alcor_pci_sdmmc_ids, |
| 1179 | .driver = { |
| 1180 | .name = DRV_NAME_ALCOR_PCI_SDMMC, |
| 1181 | .pm = &alcor_mmc_pm_ops |
| 1182 | }, |
| 1183 | }; |
| 1184 | module_platform_driver(alcor_pci_sdmmc_driver); |
| 1185 | |
| 1186 | MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>"); |
| 1187 | MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface"); |
| 1188 | MODULE_LICENSE("GPL"); |