|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * SD/MMC Greybus driver. | 
|  | * | 
|  | * Copyright 2014-2015 Google Inc. | 
|  | * Copyright 2014-2015 Linaro Ltd. | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/mmc/core.h> | 
|  | #include <linux/mmc/host.h> | 
|  | #include <linux/mmc/mmc.h> | 
|  | #include <linux/scatterlist.h> | 
|  | #include <linux/workqueue.h> | 
|  |  | 
|  | #include "greybus.h" | 
|  | #include "gbphy.h" | 
|  |  | 
|  | struct gb_sdio_host { | 
|  | struct gb_connection	*connection; | 
|  | struct gbphy_device	*gbphy_dev; | 
|  | struct mmc_host		*mmc; | 
|  | struct mmc_request	*mrq; | 
|  | struct mutex		lock;	/* lock for this host */ | 
|  | size_t			data_max; | 
|  | spinlock_t		xfer;	/* lock to cancel ongoing transfer */ | 
|  | bool			xfer_stop; | 
|  | struct workqueue_struct	*mrq_workqueue; | 
|  | struct work_struct	mrqwork; | 
|  | u8			queued_events; | 
|  | bool			removed; | 
|  | bool			card_present; | 
|  | bool			read_only; | 
|  | }; | 
|  |  | 
|  |  | 
|  | #define GB_SDIO_RSP_R1_R5_R6_R7	(GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ | 
|  | GB_SDIO_RSP_OPCODE) | 
|  | #define GB_SDIO_RSP_R3_R4	(GB_SDIO_RSP_PRESENT) | 
|  | #define GB_SDIO_RSP_R2		(GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ | 
|  | GB_SDIO_RSP_136) | 
|  | #define GB_SDIO_RSP_R1B		(GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ | 
|  | GB_SDIO_RSP_OPCODE | GB_SDIO_RSP_BUSY) | 
|  |  | 
|  | /* kernel vdd starts at 0x80 and we need to translate to greybus ones 0x01 */ | 
|  | #define GB_SDIO_VDD_SHIFT	8 | 
|  |  | 
|  | #ifndef MMC_CAP2_CORE_RUNTIME_PM | 
|  | #define MMC_CAP2_CORE_RUNTIME_PM	0 | 
|  | #endif | 
|  |  | 
|  | static inline bool single_op(struct mmc_command *cmd) | 
|  | { | 
|  | u32 opcode = cmd->opcode; | 
|  |  | 
|  | return opcode == MMC_WRITE_BLOCK || | 
|  | opcode == MMC_READ_SINGLE_BLOCK; | 
|  | } | 
|  |  | 
|  | static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) | 
|  | { | 
|  | u32 caps = 0; | 
|  | u32 caps2 = 0; | 
|  |  | 
|  | caps = ((r & GB_SDIO_CAP_NONREMOVABLE) ? MMC_CAP_NONREMOVABLE : 0) | | 
|  | ((r & GB_SDIO_CAP_4_BIT_DATA) ? MMC_CAP_4_BIT_DATA : 0) | | 
|  | ((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) | | 
|  | ((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) | | 
|  | ((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) | | 
|  | ((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) | | 
|  | ((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) | | 
|  | ((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) | | 
|  | ((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) | | 
|  | ((r & GB_SDIO_CAP_UHS_SDR12) ? MMC_CAP_UHS_SDR12 : 0) | | 
|  | ((r & GB_SDIO_CAP_UHS_SDR25) ? MMC_CAP_UHS_SDR25 : 0) | | 
|  | ((r & GB_SDIO_CAP_UHS_SDR50) ? MMC_CAP_UHS_SDR50 : 0) | | 
|  | ((r & GB_SDIO_CAP_UHS_SDR104) ? MMC_CAP_UHS_SDR104 : 0) | | 
|  | ((r & GB_SDIO_CAP_UHS_DDR50) ? MMC_CAP_UHS_DDR50 : 0) | | 
|  | ((r & GB_SDIO_CAP_DRIVER_TYPE_A) ? MMC_CAP_DRIVER_TYPE_A : 0) | | 
|  | ((r & GB_SDIO_CAP_DRIVER_TYPE_C) ? MMC_CAP_DRIVER_TYPE_C : 0) | | 
|  | ((r & GB_SDIO_CAP_DRIVER_TYPE_D) ? MMC_CAP_DRIVER_TYPE_D : 0); | 
|  |  | 
|  | caps2 = ((r & GB_SDIO_CAP_HS200_1_2V) ? MMC_CAP2_HS200_1_2V_SDR : 0) | | 
|  | ((r & GB_SDIO_CAP_HS400_1_2V) ? MMC_CAP2_HS400_1_2V : 0) | | 
|  | ((r & GB_SDIO_CAP_HS400_1_8V) ? MMC_CAP2_HS400_1_8V : 0) | | 
|  | ((r & GB_SDIO_CAP_HS200_1_8V) ? MMC_CAP2_HS200_1_8V_SDR : 0); | 
|  |  | 
|  | host->mmc->caps = caps; | 
|  | host->mmc->caps2 = caps2 | MMC_CAP2_CORE_RUNTIME_PM; | 
|  |  | 
|  | if (caps & MMC_CAP_NONREMOVABLE) | 
|  | host->card_present = true; | 
|  | } | 
|  |  | 
|  | static u32 _gb_sdio_get_host_ocr(u32 ocr) | 
|  | { | 
|  | return (((ocr & GB_SDIO_VDD_165_195) ? MMC_VDD_165_195 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_20_21) ? MMC_VDD_20_21 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_21_22) ? MMC_VDD_21_22 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_22_23) ? MMC_VDD_22_23 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_23_24) ? MMC_VDD_23_24 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_24_25) ? MMC_VDD_24_25 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_25_26) ? MMC_VDD_25_26 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_26_27) ? MMC_VDD_26_27 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_27_28) ? MMC_VDD_27_28 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_28_29) ? MMC_VDD_28_29 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_29_30) ? MMC_VDD_29_30 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_30_31) ? MMC_VDD_30_31 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_31_32) ? MMC_VDD_31_32 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_32_33) ? MMC_VDD_32_33 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_33_34) ? MMC_VDD_33_34 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_34_35) ? MMC_VDD_34_35 : 0) | | 
|  | ((ocr & GB_SDIO_VDD_35_36) ? MMC_VDD_35_36 : 0) | 
|  | ); | 
|  | } | 
|  |  | 
|  | static int gb_sdio_get_caps(struct gb_sdio_host *host) | 
|  | { | 
|  | struct gb_sdio_get_caps_response response; | 
|  | struct mmc_host *mmc = host->mmc; | 
|  | u16 data_max; | 
|  | u32 blksz; | 
|  | u32 ocr; | 
|  | u32 r; | 
|  | int ret; | 
|  |  | 
|  | ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_GET_CAPABILITIES, | 
|  | NULL, 0, &response, sizeof(response)); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | r = le32_to_cpu(response.caps); | 
|  |  | 
|  | _gb_sdio_set_host_caps(host, r); | 
|  |  | 
|  | /* get the max block size that could fit our payload */ | 
|  | data_max = gb_operation_get_payload_size_max(host->connection); | 
|  | data_max = min(data_max - sizeof(struct gb_sdio_transfer_request), | 
|  | data_max - sizeof(struct gb_sdio_transfer_response)); | 
|  |  | 
|  | blksz = min_t(u16, le16_to_cpu(response.max_blk_size), data_max); | 
|  | blksz = max_t(u32, 512, blksz); | 
|  |  | 
|  | mmc->max_blk_size = rounddown_pow_of_two(blksz); | 
|  | mmc->max_blk_count = le16_to_cpu(response.max_blk_count); | 
|  | host->data_max = data_max; | 
|  |  | 
|  | /* get ocr supported values */ | 
|  | ocr = _gb_sdio_get_host_ocr(le32_to_cpu(response.ocr)); | 
|  | mmc->ocr_avail = ocr; | 
|  | mmc->ocr_avail_sdio = mmc->ocr_avail; | 
|  | mmc->ocr_avail_sd = mmc->ocr_avail; | 
|  | mmc->ocr_avail_mmc = mmc->ocr_avail; | 
|  |  | 
|  | /* get frequency range values */ | 
|  | mmc->f_min = le32_to_cpu(response.f_min); | 
|  | mmc->f_max = le32_to_cpu(response.f_max); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void _gb_queue_event(struct gb_sdio_host *host, u8 event) | 
|  | { | 
|  | if (event & GB_SDIO_CARD_INSERTED) | 
|  | host->queued_events &= ~GB_SDIO_CARD_REMOVED; | 
|  | else if (event & GB_SDIO_CARD_REMOVED) | 
|  | host->queued_events &= ~GB_SDIO_CARD_INSERTED; | 
|  |  | 
|  | host->queued_events |= event; | 
|  | } | 
|  |  | 
|  | static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) | 
|  | { | 
|  | u8 state_changed = 0; | 
|  |  | 
|  | if (event & GB_SDIO_CARD_INSERTED) { | 
|  | if (host->mmc->caps & MMC_CAP_NONREMOVABLE) | 
|  | return 0; | 
|  | if (host->card_present) | 
|  | return 0; | 
|  | host->card_present = true; | 
|  | state_changed = 1; | 
|  | } | 
|  |  | 
|  | if (event & GB_SDIO_CARD_REMOVED) { | 
|  | if (host->mmc->caps & MMC_CAP_NONREMOVABLE) | 
|  | return 0; | 
|  | if (!(host->card_present)) | 
|  | return 0; | 
|  | host->card_present = false; | 
|  | state_changed = 1; | 
|  | } | 
|  |  | 
|  | if (event & GB_SDIO_WP) | 
|  | host->read_only = true; | 
|  |  | 
|  | if (state_changed) { | 
|  | dev_info(mmc_dev(host->mmc), "card %s now event\n", | 
|  | (host->card_present ?  "inserted" : "removed")); | 
|  | mmc_detect_change(host->mmc, 0); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gb_sdio_request_handler(struct gb_operation *op) | 
|  | { | 
|  | struct gb_sdio_host *host = gb_connection_get_data(op->connection); | 
|  | struct gb_message *request; | 
|  | struct gb_sdio_event_request *payload; | 
|  | u8 type = op->type; | 
|  | int ret =  0; | 
|  | u8 event; | 
|  |  | 
|  | if (type != GB_SDIO_TYPE_EVENT) { | 
|  | dev_err(mmc_dev(host->mmc), | 
|  | "unsupported unsolicited event: %u\n", type); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | request = op->request; | 
|  |  | 
|  | if (request->payload_size < sizeof(*payload)) { | 
|  | dev_err(mmc_dev(host->mmc), "wrong event size received (%zu < %zu)\n", | 
|  | request->payload_size, sizeof(*payload)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | payload = request->payload; | 
|  | event = payload->event; | 
|  |  | 
|  | if (host->removed) | 
|  | _gb_queue_event(host, event); | 
|  | else | 
|  | ret = _gb_sdio_process_events(host, event); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int gb_sdio_set_ios(struct gb_sdio_host *host, | 
|  | struct gb_sdio_set_ios_request *request) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = gbphy_runtime_get_sync(host->gbphy_dev); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_SET_IOS, request, | 
|  | sizeof(*request), NULL, 0); | 
|  |  | 
|  | gbphy_runtime_put_autosuspend(host->gbphy_dev); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, | 
|  | size_t len, u16 nblocks, off_t skip) | 
|  | { | 
|  | struct gb_sdio_transfer_request *request; | 
|  | struct gb_sdio_transfer_response *response; | 
|  | struct gb_operation *operation; | 
|  | struct scatterlist *sg = data->sg; | 
|  | unsigned int sg_len = data->sg_len; | 
|  | size_t copied; | 
|  | u16 send_blksz; | 
|  | u16 send_blocks; | 
|  | int ret; | 
|  |  | 
|  | WARN_ON(len > host->data_max); | 
|  |  | 
|  | operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, | 
|  | len + sizeof(*request), | 
|  | sizeof(*response), GFP_KERNEL); | 
|  | if (!operation) | 
|  | return -ENOMEM; | 
|  |  | 
|  | request = operation->request->payload; | 
|  | request->data_flags = data->flags >> 8; | 
|  | request->data_blocks = cpu_to_le16(nblocks); | 
|  | request->data_blksz = cpu_to_le16(data->blksz); | 
|  |  | 
|  | copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0], len, skip); | 
|  |  | 
|  | if (copied != len) { | 
|  | ret = -EINVAL; | 
|  | goto err_put_operation; | 
|  | } | 
|  |  | 
|  | ret = gb_operation_request_send_sync(operation); | 
|  | if (ret < 0) | 
|  | goto err_put_operation; | 
|  |  | 
|  | response = operation->response->payload; | 
|  |  | 
|  | send_blocks = le16_to_cpu(response->data_blocks); | 
|  | send_blksz = le16_to_cpu(response->data_blksz); | 
|  |  | 
|  | if (len != send_blksz * send_blocks) { | 
|  | dev_err(mmc_dev(host->mmc), "send: size received: %zu != %d\n", | 
|  | len, send_blksz * send_blocks); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | err_put_operation: | 
|  | gb_operation_put(operation); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, | 
|  | size_t len, u16 nblocks, off_t skip) | 
|  | { | 
|  | struct gb_sdio_transfer_request *request; | 
|  | struct gb_sdio_transfer_response *response; | 
|  | struct gb_operation *operation; | 
|  | struct scatterlist *sg = data->sg; | 
|  | unsigned int sg_len = data->sg_len; | 
|  | size_t copied; | 
|  | u16 recv_blksz; | 
|  | u16 recv_blocks; | 
|  | int ret; | 
|  |  | 
|  | WARN_ON(len > host->data_max); | 
|  |  | 
|  | operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, | 
|  | sizeof(*request), | 
|  | len + sizeof(*response), GFP_KERNEL); | 
|  | if (!operation) | 
|  | return -ENOMEM; | 
|  |  | 
|  | request = operation->request->payload; | 
|  | request->data_flags = data->flags >> 8; | 
|  | request->data_blocks = cpu_to_le16(nblocks); | 
|  | request->data_blksz = cpu_to_le16(data->blksz); | 
|  |  | 
|  | ret = gb_operation_request_send_sync(operation); | 
|  | if (ret < 0) | 
|  | goto err_put_operation; | 
|  |  | 
|  | response = operation->response->payload; | 
|  | recv_blocks = le16_to_cpu(response->data_blocks); | 
|  | recv_blksz = le16_to_cpu(response->data_blksz); | 
|  |  | 
|  | if (len != recv_blksz * recv_blocks) { | 
|  | dev_err(mmc_dev(host->mmc), "recv: size received: %d != %zu\n", | 
|  | recv_blksz * recv_blocks, len); | 
|  | ret = -EINVAL; | 
|  | goto err_put_operation; | 
|  | } | 
|  |  | 
|  | copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0], len, | 
|  | skip); | 
|  | if (copied != len) | 
|  | ret = -EINVAL; | 
|  |  | 
|  | err_put_operation: | 
|  | gb_operation_put(operation); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) | 
|  | { | 
|  | size_t left, len; | 
|  | off_t skip = 0; | 
|  | int ret = 0; | 
|  | u16 nblocks; | 
|  |  | 
|  | if (single_op(data->mrq->cmd) && data->blocks > 1) { | 
|  | ret = -ETIMEDOUT; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | left = data->blksz * data->blocks; | 
|  |  | 
|  | while (left) { | 
|  | /* check is a stop transmission is pending */ | 
|  | spin_lock(&host->xfer); | 
|  | if (host->xfer_stop) { | 
|  | host->xfer_stop = false; | 
|  | spin_unlock(&host->xfer); | 
|  | ret = -EINTR; | 
|  | goto out; | 
|  | } | 
|  | spin_unlock(&host->xfer); | 
|  | len = min(left, host->data_max); | 
|  | nblocks = len / data->blksz; | 
|  | len = nblocks * data->blksz; | 
|  |  | 
|  | if (data->flags & MMC_DATA_READ) { | 
|  | ret = _gb_sdio_recv(host, data, len, nblocks, skip); | 
|  | if (ret < 0) | 
|  | goto out; | 
|  | } else { | 
|  | ret = _gb_sdio_send(host, data, len, nblocks, skip); | 
|  | if (ret < 0) | 
|  | goto out; | 
|  | } | 
|  | data->bytes_xfered += len; | 
|  | left -= len; | 
|  | skip += len; | 
|  | } | 
|  |  | 
|  | out: | 
|  | data->error = ret; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) | 
|  | { | 
|  | struct gb_sdio_command_request request = {0}; | 
|  | struct gb_sdio_command_response response; | 
|  | struct mmc_data *data = host->mrq->data; | 
|  | u8 cmd_flags; | 
|  | u8 cmd_type; | 
|  | int i; | 
|  | int ret; | 
|  |  | 
|  | switch (mmc_resp_type(cmd)) { | 
|  | case MMC_RSP_NONE: | 
|  | cmd_flags = GB_SDIO_RSP_NONE; | 
|  | break; | 
|  | case MMC_RSP_R1: | 
|  | cmd_flags = GB_SDIO_RSP_R1_R5_R6_R7; | 
|  | break; | 
|  | case MMC_RSP_R1B: | 
|  | cmd_flags = GB_SDIO_RSP_R1B; | 
|  | break; | 
|  | case MMC_RSP_R2: | 
|  | cmd_flags = GB_SDIO_RSP_R2; | 
|  | break; | 
|  | case MMC_RSP_R3: | 
|  | cmd_flags = GB_SDIO_RSP_R3_R4; | 
|  | break; | 
|  | default: | 
|  | dev_err(mmc_dev(host->mmc), "cmd flag invalid 0x%04x\n", | 
|  | mmc_resp_type(cmd)); | 
|  | ret = -EINVAL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | switch (mmc_cmd_type(cmd)) { | 
|  | case MMC_CMD_BC: | 
|  | cmd_type = GB_SDIO_CMD_BC; | 
|  | break; | 
|  | case MMC_CMD_BCR: | 
|  | cmd_type = GB_SDIO_CMD_BCR; | 
|  | break; | 
|  | case MMC_CMD_AC: | 
|  | cmd_type = GB_SDIO_CMD_AC; | 
|  | break; | 
|  | case MMC_CMD_ADTC: | 
|  | cmd_type = GB_SDIO_CMD_ADTC; | 
|  | break; | 
|  | default: | 
|  | dev_err(mmc_dev(host->mmc), "cmd type invalid 0x%04x\n", | 
|  | mmc_cmd_type(cmd)); | 
|  | ret = -EINVAL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | request.cmd = cmd->opcode; | 
|  | request.cmd_flags = cmd_flags; | 
|  | request.cmd_type = cmd_type; | 
|  | request.cmd_arg = cpu_to_le32(cmd->arg); | 
|  | /* some controllers need to know at command time data details */ | 
|  | if (data) { | 
|  | request.data_blocks = cpu_to_le16(data->blocks); | 
|  | request.data_blksz = cpu_to_le16(data->blksz); | 
|  | } | 
|  |  | 
|  | ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND, | 
|  | &request, sizeof(request), &response, | 
|  | sizeof(response)); | 
|  | if (ret < 0) | 
|  | goto out; | 
|  |  | 
|  | /* no response expected */ | 
|  | if (cmd_flags == GB_SDIO_RSP_NONE) | 
|  | goto out; | 
|  |  | 
|  | /* long response expected */ | 
|  | if (cmd_flags & GB_SDIO_RSP_R2) | 
|  | for (i = 0; i < 4; i++) | 
|  | cmd->resp[i] = le32_to_cpu(response.resp[i]); | 
|  | else | 
|  | cmd->resp[0] = le32_to_cpu(response.resp[0]); | 
|  |  | 
|  | out: | 
|  | cmd->error = ret; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void gb_sdio_mrq_work(struct work_struct *work) | 
|  | { | 
|  | struct gb_sdio_host *host; | 
|  | struct mmc_request *mrq; | 
|  | int ret; | 
|  |  | 
|  | host = container_of(work, struct gb_sdio_host, mrqwork); | 
|  |  | 
|  | ret = gbphy_runtime_get_sync(host->gbphy_dev); | 
|  | if (ret) | 
|  | return; | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  | mrq = host->mrq; | 
|  | if (!mrq) { | 
|  | mutex_unlock(&host->lock); | 
|  | gbphy_runtime_put_autosuspend(host->gbphy_dev); | 
|  | dev_err(mmc_dev(host->mmc), "mmc request is NULL"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (host->removed) { | 
|  | mrq->cmd->error = -ESHUTDOWN; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | if (mrq->sbc) { | 
|  | ret = gb_sdio_command(host, mrq->sbc); | 
|  | if (ret < 0) | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | ret = gb_sdio_command(host, mrq->cmd); | 
|  | if (ret < 0) | 
|  | goto done; | 
|  |  | 
|  | if (mrq->data) { | 
|  | ret = gb_sdio_transfer(host, mrq->data); | 
|  | if (ret < 0) | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | if (mrq->stop) { | 
|  | ret = gb_sdio_command(host, mrq->stop); | 
|  | if (ret < 0) | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | done: | 
|  | host->mrq = NULL; | 
|  | mutex_unlock(&host->lock); | 
|  | mmc_request_done(host->mmc, mrq); | 
|  | gbphy_runtime_put_autosuspend(host->gbphy_dev); | 
|  | } | 
|  |  | 
|  | static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) | 
|  | { | 
|  | struct gb_sdio_host *host = mmc_priv(mmc); | 
|  | struct mmc_command *cmd = mrq->cmd; | 
|  |  | 
|  | /* Check if it is a cancel to ongoing transfer */ | 
|  | if (cmd->opcode == MMC_STOP_TRANSMISSION) { | 
|  | spin_lock(&host->xfer); | 
|  | host->xfer_stop = true; | 
|  | spin_unlock(&host->xfer); | 
|  | } | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  |  | 
|  | WARN_ON(host->mrq); | 
|  | host->mrq = mrq; | 
|  |  | 
|  | if (host->removed) { | 
|  | mrq->cmd->error = -ESHUTDOWN; | 
|  | goto out; | 
|  | } | 
|  | if (!host->card_present) { | 
|  | mrq->cmd->error = -ENOMEDIUM; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | queue_work(host->mrq_workqueue, &host->mrqwork); | 
|  |  | 
|  | mutex_unlock(&host->lock); | 
|  | return; | 
|  |  | 
|  | out: | 
|  | host->mrq = NULL; | 
|  | mutex_unlock(&host->lock); | 
|  | mmc_request_done(mmc, mrq); | 
|  | } | 
|  |  | 
|  | static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 
|  | { | 
|  | struct gb_sdio_host *host = mmc_priv(mmc); | 
|  | struct gb_sdio_set_ios_request request; | 
|  | int ret; | 
|  | u8 power_mode; | 
|  | u8 bus_width; | 
|  | u8 timing; | 
|  | u8 signal_voltage; | 
|  | u8 drv_type; | 
|  | u32 vdd = 0; | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  | request.clock = cpu_to_le32(ios->clock); | 
|  |  | 
|  | if (ios->vdd) | 
|  | vdd = 1 << (ios->vdd - GB_SDIO_VDD_SHIFT); | 
|  | request.vdd = cpu_to_le32(vdd); | 
|  |  | 
|  | request.bus_mode = ios->bus_mode == MMC_BUSMODE_OPENDRAIN ? | 
|  | GB_SDIO_BUSMODE_OPENDRAIN : | 
|  | GB_SDIO_BUSMODE_PUSHPULL; | 
|  |  | 
|  | switch (ios->power_mode) { | 
|  | case MMC_POWER_OFF: | 
|  | default: | 
|  | power_mode = GB_SDIO_POWER_OFF; | 
|  | break; | 
|  | case MMC_POWER_UP: | 
|  | power_mode = GB_SDIO_POWER_UP; | 
|  | break; | 
|  | case MMC_POWER_ON: | 
|  | power_mode = GB_SDIO_POWER_ON; | 
|  | break; | 
|  | case MMC_POWER_UNDEFINED: | 
|  | power_mode = GB_SDIO_POWER_UNDEFINED; | 
|  | break; | 
|  | } | 
|  | request.power_mode = power_mode; | 
|  |  | 
|  | switch (ios->bus_width) { | 
|  | case MMC_BUS_WIDTH_1: | 
|  | bus_width = GB_SDIO_BUS_WIDTH_1; | 
|  | break; | 
|  | case MMC_BUS_WIDTH_4: | 
|  | default: | 
|  | bus_width = GB_SDIO_BUS_WIDTH_4; | 
|  | break; | 
|  | case MMC_BUS_WIDTH_8: | 
|  | bus_width = GB_SDIO_BUS_WIDTH_8; | 
|  | break; | 
|  | } | 
|  | request.bus_width = bus_width; | 
|  |  | 
|  | switch (ios->timing) { | 
|  | case MMC_TIMING_LEGACY: | 
|  | default: | 
|  | timing = GB_SDIO_TIMING_LEGACY; | 
|  | break; | 
|  | case MMC_TIMING_MMC_HS: | 
|  | timing = GB_SDIO_TIMING_MMC_HS; | 
|  | break; | 
|  | case MMC_TIMING_SD_HS: | 
|  | timing = GB_SDIO_TIMING_SD_HS; | 
|  | break; | 
|  | case MMC_TIMING_UHS_SDR12: | 
|  | timing = GB_SDIO_TIMING_UHS_SDR12; | 
|  | break; | 
|  | case MMC_TIMING_UHS_SDR25: | 
|  | timing = GB_SDIO_TIMING_UHS_SDR25; | 
|  | break; | 
|  | case MMC_TIMING_UHS_SDR50: | 
|  | timing = GB_SDIO_TIMING_UHS_SDR50; | 
|  | break; | 
|  | case MMC_TIMING_UHS_SDR104: | 
|  | timing = GB_SDIO_TIMING_UHS_SDR104; | 
|  | break; | 
|  | case MMC_TIMING_UHS_DDR50: | 
|  | timing = GB_SDIO_TIMING_UHS_DDR50; | 
|  | break; | 
|  | case MMC_TIMING_MMC_DDR52: | 
|  | timing = GB_SDIO_TIMING_MMC_DDR52; | 
|  | break; | 
|  | case MMC_TIMING_MMC_HS200: | 
|  | timing = GB_SDIO_TIMING_MMC_HS200; | 
|  | break; | 
|  | case MMC_TIMING_MMC_HS400: | 
|  | timing = GB_SDIO_TIMING_MMC_HS400; | 
|  | break; | 
|  | } | 
|  | request.timing = timing; | 
|  |  | 
|  | switch (ios->signal_voltage) { | 
|  | case MMC_SIGNAL_VOLTAGE_330: | 
|  | signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_330; | 
|  | break; | 
|  | case MMC_SIGNAL_VOLTAGE_180: | 
|  | default: | 
|  | signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_180; | 
|  | break; | 
|  | case MMC_SIGNAL_VOLTAGE_120: | 
|  | signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_120; | 
|  | break; | 
|  | } | 
|  | request.signal_voltage = signal_voltage; | 
|  |  | 
|  | switch (ios->drv_type) { | 
|  | case MMC_SET_DRIVER_TYPE_A: | 
|  | drv_type = GB_SDIO_SET_DRIVER_TYPE_A; | 
|  | break; | 
|  | case MMC_SET_DRIVER_TYPE_C: | 
|  | drv_type = GB_SDIO_SET_DRIVER_TYPE_C; | 
|  | break; | 
|  | case MMC_SET_DRIVER_TYPE_D: | 
|  | drv_type = GB_SDIO_SET_DRIVER_TYPE_D; | 
|  | break; | 
|  | case MMC_SET_DRIVER_TYPE_B: | 
|  | default: | 
|  | drv_type = GB_SDIO_SET_DRIVER_TYPE_B; | 
|  | break; | 
|  | } | 
|  | request.drv_type = drv_type; | 
|  |  | 
|  | ret = gb_sdio_set_ios(host, &request); | 
|  | if (ret < 0) | 
|  | goto out; | 
|  |  | 
|  | memcpy(&mmc->ios, ios, sizeof(mmc->ios)); | 
|  |  | 
|  | out: | 
|  | mutex_unlock(&host->lock); | 
|  | } | 
|  |  | 
|  | static int gb_mmc_get_ro(struct mmc_host *mmc) | 
|  | { | 
|  | struct gb_sdio_host *host = mmc_priv(mmc); | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  | if (host->removed) { | 
|  | mutex_unlock(&host->lock); | 
|  | return -ESHUTDOWN; | 
|  | } | 
|  | mutex_unlock(&host->lock); | 
|  |  | 
|  | return host->read_only; | 
|  | } | 
|  |  | 
|  | static int gb_mmc_get_cd(struct mmc_host *mmc) | 
|  | { | 
|  | struct gb_sdio_host *host = mmc_priv(mmc); | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  | if (host->removed) { | 
|  | mutex_unlock(&host->lock); | 
|  | return -ESHUTDOWN; | 
|  | } | 
|  | mutex_unlock(&host->lock); | 
|  |  | 
|  | return host->card_present; | 
|  | } | 
|  |  | 
|  | static int gb_mmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct mmc_host_ops gb_sdio_ops = { | 
|  | .request	= gb_mmc_request, | 
|  | .set_ios	= gb_mmc_set_ios, | 
|  | .get_ro		= gb_mmc_get_ro, | 
|  | .get_cd		= gb_mmc_get_cd, | 
|  | .start_signal_voltage_switch	= gb_mmc_switch_voltage, | 
|  | }; | 
|  |  | 
|  | static int gb_sdio_probe(struct gbphy_device *gbphy_dev, | 
|  | const struct gbphy_device_id *id) | 
|  | { | 
|  | struct gb_connection *connection; | 
|  | struct mmc_host *mmc; | 
|  | struct gb_sdio_host *host; | 
|  | int ret = 0; | 
|  |  | 
|  | mmc = mmc_alloc_host(sizeof(*host), &gbphy_dev->dev); | 
|  | if (!mmc) | 
|  | return -ENOMEM; | 
|  |  | 
|  | connection = gb_connection_create(gbphy_dev->bundle, | 
|  | le16_to_cpu(gbphy_dev->cport_desc->id), | 
|  | gb_sdio_request_handler); | 
|  | if (IS_ERR(connection)) { | 
|  | ret = PTR_ERR(connection); | 
|  | goto exit_mmc_free; | 
|  | } | 
|  |  | 
|  | host = mmc_priv(mmc); | 
|  | host->mmc = mmc; | 
|  | host->removed = true; | 
|  |  | 
|  | host->connection = connection; | 
|  | gb_connection_set_data(connection, host); | 
|  | host->gbphy_dev = gbphy_dev; | 
|  | gb_gbphy_set_data(gbphy_dev, host); | 
|  |  | 
|  | ret = gb_connection_enable_tx(connection); | 
|  | if (ret) | 
|  | goto exit_connection_destroy; | 
|  |  | 
|  | ret = gb_sdio_get_caps(host); | 
|  | if (ret < 0) | 
|  | goto exit_connection_disable; | 
|  |  | 
|  | mmc->ops = &gb_sdio_ops; | 
|  |  | 
|  | mmc->max_segs = host->mmc->max_blk_count; | 
|  |  | 
|  | /* for now we make a map 1:1 between max request and segment size */ | 
|  | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | 
|  | mmc->max_seg_size = mmc->max_req_size; | 
|  |  | 
|  | mutex_init(&host->lock); | 
|  | spin_lock_init(&host->xfer); | 
|  | host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, | 
|  | dev_name(&gbphy_dev->dev)); | 
|  | if (!host->mrq_workqueue) { | 
|  | ret = -ENOMEM; | 
|  | goto exit_connection_disable; | 
|  | } | 
|  | INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); | 
|  |  | 
|  | ret = gb_connection_enable(connection); | 
|  | if (ret) | 
|  | goto exit_wq_destroy; | 
|  |  | 
|  | ret = mmc_add_host(mmc); | 
|  | if (ret < 0) | 
|  | goto exit_wq_destroy; | 
|  | host->removed = false; | 
|  | ret = _gb_sdio_process_events(host, host->queued_events); | 
|  | host->queued_events = 0; | 
|  |  | 
|  | gbphy_runtime_put_autosuspend(gbphy_dev); | 
|  |  | 
|  | return ret; | 
|  |  | 
|  | exit_wq_destroy: | 
|  | destroy_workqueue(host->mrq_workqueue); | 
|  | exit_connection_disable: | 
|  | gb_connection_disable(connection); | 
|  | exit_connection_destroy: | 
|  | gb_connection_destroy(connection); | 
|  | exit_mmc_free: | 
|  | mmc_free_host(mmc); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void gb_sdio_remove(struct gbphy_device *gbphy_dev) | 
|  | { | 
|  | struct gb_sdio_host *host = gb_gbphy_get_data(gbphy_dev); | 
|  | struct gb_connection *connection = host->connection; | 
|  | struct mmc_host *mmc; | 
|  | int ret; | 
|  |  | 
|  | ret = gbphy_runtime_get_sync(gbphy_dev); | 
|  | if (ret) | 
|  | gbphy_runtime_get_noresume(gbphy_dev); | 
|  |  | 
|  | mutex_lock(&host->lock); | 
|  | host->removed = true; | 
|  | mmc = host->mmc; | 
|  | gb_connection_set_data(connection, NULL); | 
|  | mutex_unlock(&host->lock); | 
|  |  | 
|  | flush_workqueue(host->mrq_workqueue); | 
|  | destroy_workqueue(host->mrq_workqueue); | 
|  | gb_connection_disable_rx(connection); | 
|  | mmc_remove_host(mmc); | 
|  | gb_connection_disable(connection); | 
|  | gb_connection_destroy(connection); | 
|  | mmc_free_host(mmc); | 
|  | } | 
|  |  | 
|  | static const struct gbphy_device_id gb_sdio_id_table[] = { | 
|  | { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(gbphy, gb_sdio_id_table); | 
|  |  | 
|  | static struct gbphy_driver sdio_driver = { | 
|  | .name		= "sdio", | 
|  | .probe		= gb_sdio_probe, | 
|  | .remove		= gb_sdio_remove, | 
|  | .id_table	= gb_sdio_id_table, | 
|  | }; | 
|  |  | 
|  | module_gbphy_driver(sdio_driver); | 
|  | MODULE_LICENSE("GPL v2"); |