> From: Roger Tseng <rogera...@realtek.com> > > Realtek USB card reader provides a channel to transfer command or data to > flash > memory cards. This driver exports host instances for mmc and memstick > subsystems > and handles basic works. > > Signed-off-by: Roger Tseng <rogera...@realtek.com> > --- > drivers/mfd/Kconfig | 10 + > drivers/mfd/Makefile | 1 + > drivers/mfd/rtsx_usb.c | 788 > +++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/rtsx_usb.h | 619 +++++++++++++++++++++++++++++++++ > 4 files changed, 1418 insertions(+) > create mode 100644 drivers/mfd/rtsx_usb.c > create mode 100644 include/linux/mfd/rtsx_usb.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index b7c74a7..4c99ebd 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -507,6 +507,16 @@ config MFD_RTSX_PCI > types of memory cards, such as Memory Stick, Memory Stick Pro, > Secure Digital and MultiMediaCard. > > +config MFD_RTSX_USB > + tristate "Realtek USB card reader" > + depends on USB > + select MFD_CORE > + help > + Select this option to get support for Realtek USB 2.0 card readers > + including RTS5129, RTS5139, RTS5179 and RTS5170. > + Realtek card reader supports access to many types of memory cards, > + such as Memory Stick Pro, Secure Digital and MultiMediaCard. > +
The help section should be indented by 2 spaces. > config MFD_RC5T583 > bool "Ricoh RC5T583 Power Management system device" > depends on I2C=y > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index 8a28dc9..33b8de6 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o > > rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o > rtl8411.o rts5227.o rts5249.o > obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o > +obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o > > obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o > obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o > diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c > new file mode 100644 > index 0000000..905ec8d > --- /dev/null > +++ b/drivers/mfd/rtsx_usb.c > @@ -0,0 +1,788 @@ > +/* Driver for Realtek USB card reader > + * > + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, see <http://www.gnu.org/licenses/>. > + * > + * Author: > + * Roger Tseng <rogera...@realtek.com> > + */ > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/mutex.h> > +#include <linux/usb.h> > +#include <linux/platform_device.h> > +#include <linux/mfd/core.h> > +#include <asm/unaligned.h> > +#include <linux/mfd/rtsx_usb.h> > + > +static int polling_pipe = 1; > +module_param(polling_pipe, int, S_IRUGO | S_IWUSR); > +MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); '/n' here. > +static struct mfd_cell rtsx_usb_cells[] = { > + [RTSX_USB_SD_CARD] = { > + .name = DRV_NAME_RTSX_USB_SDMMC, > + }, > + [RTSX_USB_MS_CARD] = { > + .name = DRV_NAME_RTSX_USB_MS, > + }, > +}; I'm not entirely convinced that this is an MFD device? > +static void rtsx_usb_sg_timed_out(unsigned long data) > +{ > + struct rtsx_ucr *ucr = (struct rtsx_ucr *)data; What's going to happen when your device runs 64bit? > + dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); > + usb_sg_cancel(&ucr->current_sg); Are you sure this needs to live here? Why isn't this in drivers/usb? > + /* we know the cancellation is caused by time-out */ > + ucr->current_sg.status = -ETIMEDOUT; > +} > + > +static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, > + unsigned int pipe, struct scatterlist *sg, int num_sg, > + unsigned int length, unsigned int *act_len, int timeout) > +{ > + int ret; > + > + dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", > + __func__, length, num_sg); > + ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, > + sg, num_sg, length, GFP_NOIO); > + if (ret) > + return ret; > + > + ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); > + add_timer(&ucr->sg_timer); > + usb_sg_wait(&ucr->current_sg); > + del_timer(&ucr->sg_timer); > + > + if (act_len) > + *act_len = ucr->current_sg.bytes; > + > + return ucr->current_sg.status; > +} Code looks fine, but shouldn't this live an a USB driver? > +int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, > + void *buf, unsigned int len, int num_sg, > + unsigned int *act_len, int timeout) > +{ > + if (timeout < 600) > + timeout = 600; > + > + if (num_sg) > + return rtsx_usb_bulk_transfer_sglist(ucr, pipe, > + (struct scatterlist *)buf, num_sg, len, act_len, > + timeout); > + else > + return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, > + timeout); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); > + > +static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, > + u16 addr, u16 len, u8 *data) > +{ > + u16 cmd_len = len + 12; Why 12? Please #define. > + if (data == NULL) if (!data) > + return -EINVAL; > + > + if (cmd_len > IOBUF_SIZE) > + return -EINVAL; > + > + if (cmd_len % 4) > + cmd_len += (4 - cmd_len % 4); Please document in a comment. > + > + Extra '/n' > + ucr->cmd_buf[0] = 'R'; > + ucr->cmd_buf[1] = 'T'; > + ucr->cmd_buf[2] = 'C'; > + ucr->cmd_buf[3] = 'R'; > + ucr->cmd_buf[PACKET_TYPE] = SEQ_WRITE; > + ucr->cmd_buf[5] = (u8)(len >> 8); > + ucr->cmd_buf[6] = (u8)len; > + ucr->cmd_buf[STAGE_FLAG] = 0; > + ucr->cmd_buf[8] = (u8)(addr >> 8); > + ucr->cmd_buf[9] = (u8)addr; I think someone said there are kernel macros for this stuff. > + memcpy(ucr->cmd_buf + 12, data, len); > + > + return rtsx_usb_transfer_data(ucr, > + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), > + ucr->cmd_buf, cmd_len, 0, NULL, 100); Still think it should be a USB driver! > +} > + > +static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, > + u16 addr, u16 len, u8 *data) > +{ > + int i, ret; > + u16 rsp_len, res_len; > + > + if (data == NULL) if (!data) > + return -EINVAL; > + > + res_len = len % 4; > + rsp_len = len - res_len; > + > + /* 4-byte aligned part */ > + if (rsp_len) { > + ucr->cmd_buf[0] = 'R'; > + ucr->cmd_buf[1] = 'T'; > + ucr->cmd_buf[2] = 'C'; > + ucr->cmd_buf[3] = 'R'; > + ucr->cmd_buf[PACKET_TYPE] = SEQ_READ; > + ucr->cmd_buf[5] = (u8)(rsp_len >> 8); > + ucr->cmd_buf[6] = (u8)rsp_len; > + ucr->cmd_buf[STAGE_FLAG] = STAGE_R; > + ucr->cmd_buf[8] = (u8)(addr >> 8); > + ucr->cmd_buf[9] = (u8)addr; This looks the same as above. If so, please place in a function. > + ret = rtsx_usb_transfer_data(ucr, > + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), > + ucr->cmd_buf, 12, 0, NULL, 100); > + if (ret) > + return ret; > + > + ret = rtsx_usb_transfer_data(ucr, > + usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), > + data, rsp_len, 0, NULL, 100); > + if (ret) > + return ret; > + } > + > + /* unaligned part */ > + for (i = 0; i < res_len; i++) { > + ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, > + data + rsp_len + i); > + if (ret) > + return ret; > + } > + > + return 0; > +} '/n' here. > +int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) > +{ > + return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); > + > +int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) > +{ > + return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); > + > +int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, > + u8 mask, u8 data) > +{ > + u16 value = 0, index = 0; > + > + value |= 0x03 << 14; > + value |= addr & 0x3FFF; > + value = ((value << 8) & 0xFF00) | ((value >> 8) & 0x00FF); > + index |= (u16)mask; > + index |= (u16)data << 8; Lots of random numbers here, please #define for clarity and ease of reading. > + return usb_control_msg(ucr->pusb_dev, > + usb_sndctrlpipe(ucr->pusb_dev, 0), 0, 0x40, > + value, index, NULL, 0, 100); Same here. > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); > + > +int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) > +{ > + u16 value = 0; > + > + if (data == NULL) if (!data), etc > + return -EINVAL; > + *data = 0; > + > + value |= 0x02 << 14; > + value |= addr & 0x3FFF; > + value = ((value << 8) & 0xFF00) | ((value >> 8) & 0x00FF); Less random numbers for #defines please, etc. > + return usb_control_msg(ucr->pusb_dev, > + usb_rcvctrlpipe(ucr->pusb_dev, 0), 0, 0xC0, > + value, 0, data, 1, 100); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); > + > +void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, > + u8 cmd_type, > + u16 reg_addr, > + u8 mask, > + u8 data) This is a strange way of organising your function params. > +{ > + int i; > + > + if (ucr->cmd_idx < ((IOBUF_SIZE - CMD_OFFSET) / 4)) { I think you can drop a layer of ()'s here. > + i = CMD_OFFSET + ucr->cmd_idx * 4; > + > + ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | > + (u8)((reg_addr >> 8) & 0x3F); > + ucr->cmd_buf[i++] = (u8)reg_addr; > + ucr->cmd_buf[i++] = mask; > + ucr->cmd_buf[i++] = data; > + > + ucr->cmd_idx++; > + } > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); > + > +int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) > +{ > + int ret; > + > + ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); > + ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); > + ucr->cmd_buf[STAGE_FLAG] = flag; > + > + ret = rtsx_usb_transfer_data(ucr, > + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), > + ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, > + 0, NULL, timeout); > + if (ret) { > + rtsx_usb_clear_fsm_err(ucr); > + return ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); > + > +int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) > +{ > + if (rsp_len <= 0) > + return -EINVAL; > + > + if (rsp_len % 4) > + rsp_len += (4 - rsp_len % 4); Please document. > + return rtsx_usb_transfer_data(ucr, > + usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), > + ucr->rsp_buf, rsp_len, 0, NULL, timeout); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); > + > +static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) > +{ > + int ret; > + > + rtsx_usb_init_cmd(ucr); > + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); > + rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); > + ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); > + if (ret) > + return ret; > + > + ret = rtsx_usb_get_rsp(ucr, 2, 100); > + if (ret) > + return ret; > + > + *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | > + ((ucr->rsp_buf[1] & 0x03) << 4); > + > + return 0; > +} > + > +int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) > +{ > + int ret; > + > + if (status == NULL) > + return -EINVAL; > + > + if (polling_pipe == 0) > + ret = usb_control_msg(ucr->pusb_dev, > + usb_rcvctrlpipe(ucr->pusb_dev, 0), > + 0x02, 0xC0, 0, 0, status, 2, 100); > + else > + ret = rtsx_usb_get_status_with_bulk(ucr, status); > + > + /* usb_control_msg may return positive when success */ > + if (ret < 0) > + return ret; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); > + > +static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) > +{ > + dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", > + val, addr); > + > + rtsx_usb_init_cmd(ucr); > + > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, > + 0xFF, (addr >> 4) & 0x0F); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); > + > + return rtsx_usb_send_cmd(ucr, MODE_C, 100); > +} > + > +int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) > +{ > + rtsx_usb_init_cmd(ucr); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); > + return rtsx_usb_send_cmd(ucr, MODE_C, 100); > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_write_register); > + > +int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) > +{ > + int ret; > + > + if (data != NULL) > + *data = 0; > + > + rtsx_usb_init_cmd(ucr); > + rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); > + ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); > + if (ret) > + return ret; > + > + ret = rtsx_usb_get_rsp(ucr, 1, 100); > + if (ret) > + return ret; > + > + if (data != NULL) > + *data = ucr->rsp_buf[0]; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_read_register); > + > +static inline u8 double_ssc_depth(u8 depth) > +{ > + return ((depth > 1) ? (depth - 1) : depth); You can drop a layer of ()'s here too. > +} > + > +static u8 revise_ssc_depth(u8 ssc_depth, u8 div) > +{ > + if (div > CLK_DIV_1) { > + if (ssc_depth > (div - 1)) And here, etc. > + ssc_depth -= (div - 1); > + else > + ssc_depth = SSC_DEPTH_2M; > + } > + > + return ssc_depth; > +} > + > +int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, > + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) > +{ > + int ret; > + u8 n, clk_divider, mcu_cnt, div; > + > + if (!card_clock) { > + ucr->cur_clk = 0; > + return 0; > + } > + > + if (initial_mode) { > + /* We use 250k(around) here, in initial stage */ > + clk_divider = SD_CLK_DIVIDE_128; > + card_clock = 30000000; > + } else { > + clk_divider = SD_CLK_DIVIDE_0; > + } > + > + ret = rtsx_usb_write_register(ucr, SD_CFG1, > + SD_CLK_DIVIDE_MASK, clk_divider); > + if (ret < 0) > + return ret; > + > + card_clock /= 1000000; > + dev_dbg(&ucr->pusb_intf->dev, > + "Switch card clock to %dMHz\n", card_clock); > + > + if (!initial_mode && double_clk) > + card_clock *= 2; > + dev_dbg(&ucr->pusb_intf->dev, > + "Internal SSC clock: %dMHz (cur_clk = %d)\n", > + card_clock, ucr->cur_clk); > + > + if (card_clock == ucr->cur_clk) > + return 0; > + > + /* Converting clock value into internal settings: n and div */ > + n = card_clock - 2; > + if ((card_clock <= 2) || (n > MAX_DIV_N)) > + return -EINVAL; > + > + mcu_cnt = 60/card_clock + 3; > + if (mcu_cnt > 15) > + mcu_cnt = 15; > + > + /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ > + > + div = CLK_DIV_1; > + while (n < MIN_DIV_N && div < CLK_DIV_4) { > + n = (n + 2) * 2 - 2; > + div++; > + } > + dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); > + > + if (double_clk) > + ssc_depth = double_ssc_depth(ssc_depth); > + > + ssc_depth = revise_ssc_depth(ssc_depth, div); > + dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); > + > + rtsx_usb_init_cmd(ucr); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, > + 0x3F, (div << 4) | mcu_cnt); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, > + SSC_DEPTH_MASK, ssc_depth); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); > + if (vpclk) { > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, > + PHASE_NOT_RESET, 0); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, > + PHASE_NOT_RESET, PHASE_NOT_RESET); > + } > + > + ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); > + if (ret < 0) > + return ret; > + > + ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, > + SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); > + if (ret < 0) > + return ret; > + > + /* Wait SSC clock stable */ > + usleep_range(100, 1000); > + > + ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); > + if (ret < 0) > + return ret; > + > + ucr->cur_clk = card_clock; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); > + > +int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) > +{ > + int ret; > + u16 val; > + u16 cd_mask[] = { > + [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), > + [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) > + }; > + > + ret = rtsx_usb_get_card_status(ucr, &val); > + /* > + * If get status fails, return 0 (ok) for the exclusive check > + * and let the flow fail at somewhere else. > + */ > + if (ret) > + return 0; > + > + if (val & cd_mask[card]) > + return -EIO; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); > + > +static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) > +{ > + int ret; > + u8 val; > + > + rtsx_usb_init_cmd(ucr); > + > + if (CHECK_PKG(ucr, LQFP48)) { > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, > + LDO3318_PWR_MASK, LDO_SUSPEND); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, > + FORCE_LDO_POWERB, FORCE_LDO_POWERB); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, > + 0x30, 0x10); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, > + 0x03, 0x01); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, > + 0x0C, 0x04); > + } > + > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, > + CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, > + SD30_DRIVE_MASK, DRIVER_TYPE_D); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, > + CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); > + > + if (ucr->is_rts5179) > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, > + CARD_PULL_CTL5, 0x03, 0x01); > + > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, > + EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); > + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, > + XD_INT | MS_INT | SD_INT, > + XD_INT | MS_INT | SD_INT); > + > + ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); > + if (ret) > + return ret; > + > + /* config non-crystal mode */ > + rtsx_usb_read_register(ucr, CFG_MODE, &val); > + if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { > + ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) > +{ > + int ret; > + u8 val; > + > + rtsx_usb_clear_fsm_err(ucr); > + > + /* power on SSC*/ White space error. > + ret = rtsx_usb_write_register(ucr, > + FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); > + if (ret) > + return ret; > + > + usleep_range(100, 1000); > + ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); > + if (ret) > + return ret; > + > + /* determine IC version */ > + ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); > + if (ret) > + return ret; > + > + ucr->ic_version = val & HW_VER_MASK; > + > + /* determine package */ > + ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); > + if (ret) > + return ret; '/n' here. > + if (val & CARD_SHARE_LQFP_SEL) { > + ucr->package = LQFP48; > + dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); > + } else { > + ucr->package = QFN24; > + dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); > + } > + > + /* determine IC variations */ > + rtsx_usb_read_register(ucr, CFG_MODE_1, &val); > + if (val & RTS5179) { > + ucr->is_rts5179 = 1; > + dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); > + } else { > + ucr->is_rts5179 = 0; These are better as bools I think. > + } > + > + ret = rtsx_usb_reset_chip(ucr); > + if (ret) > + return ret; > + > + return 0; Just return rtsx_usb_reset_chip(ucr); > +} > + > +static int rtsx_usb_probe(struct usb_interface *intf, > + const struct usb_device_id *id) > +{ > + struct usb_device *usb_dev = interface_to_usbdev(intf); > + struct rtsx_ucr *ucr; > + int i; > + int ret; > + > + dev_dbg(&intf->dev, > + ": Realtek USB Card Reader found at bus %03d address %03d\n", > + usb_dev->bus->busnum, usb_dev->devnum); > + > + ucr = kzalloc(sizeof(struct rtsx_ucr), GFP_KERNEL); s/struct rtsx_ucr/*ucr/ Any reason for not using managed resources? > + if (!ucr) > + return -ENOMEM; > + > + ucr->pusb_dev = usb_dev; > + > + /* alloc coherent buffer */ > + ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, > + GFP_KERNEL, &ucr->iobuf_dma); > + if (!ucr->iobuf) { > + ret = -ENOMEM; > + goto out_free_chip; No need to do this if you use managed resources. > + } > + > + /* set USB interface data */ > + usb_set_intfdata(intf, ucr); > + > + ucr->vendor_id = id->idVendor; > + ucr->product_id = id->idProduct; > + ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; > + > + mutex_init(&ucr->dev_mutex); > + > + ucr->pusb_intf = intf; > + > + /* initialize */ > + ret = rtsx_usb_init_chip(ucr); > + if (ret) > + goto out_init_fail; > + > + for (i = 0; i < ARRAY_SIZE(rtsx_usb_cells); i++) { > + rtsx_usb_cells[i].platform_data = &ucr; You've already put this in your drvdata (or ntfdata, as it's called here). Why do you also need it in platform_data? > + rtsx_usb_cells[i].pdata_size = sizeof(*ucr); > + } > + ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells, > + ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL); > + if (ret) > + goto out_init_fail; > + /* initialize USB SG transfer timer */ > + init_timer(&ucr->sg_timer); > + setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); > +#ifdef CONFIG_PM > + intf->needs_remote_wakeup = 1; > + usb_enable_autosuspend(usb_dev); > +#endif > + > + return 0; > + > +out_init_fail: > + usb_set_intfdata(ucr->pusb_intf, NULL); No need to do this, it's taken care of elsewhere. > + usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, > + ucr->iobuf_dma); > + > +out_free_chip: > + kfree(ucr); > + dev_err(&intf->dev, "%s failed\n", __func__); > + return ret; > +} > + > +static void rtsx_usb_disconnect(struct usb_interface *intf) > +{ > + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); > + > + dev_dbg(&intf->dev, "%s called\n", __func__); > + > + mfd_remove_devices(&intf->dev); > + > + usb_set_intfdata(ucr->pusb_intf, NULL); > + usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, > + ucr->iobuf_dma); > + > + kfree(ucr); > +} > + > +#ifdef CONFIG_PM > +static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) > +{ > + struct rtsx_ucr *ucr = > + (struct rtsx_ucr *)usb_get_intfdata(intf); > + > + dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n", > + __func__, message.event); > + > + mutex_lock(&ucr->dev_mutex); > + rtsx_usb_turn_off_led(ucr); That's it? That's all you do during suspend? :) > + mutex_unlock(&ucr->dev_mutex); > + return 0; > +} > + > +static int rtsx_usb_resume(struct usb_interface *intf) > +{ Don't you want to turn the LED back on here? Or will that happen automatically when you write/read to/from it again? > + return 0; > +} > + > +static int rtsx_usb_reset_resume(struct usb_interface *intf) > +{ > + struct rtsx_ucr *ucr = > + (struct rtsx_ucr *)usb_get_intfdata(intf); > + > + rtsx_usb_reset_chip(ucr); > + return 0; > +} > + > +#else /* CONFIG_PM */ > + > +#define rtsx_usb_suspend NULL > +#define rtsx_usb_resume NULL > +#define rtsx_usb_reset_resume NULL > + > +#endif /* CONFIG_PM */ > + > + > +static int rtsx_usb_pre_reset(struct usb_interface *intf) > +{ > + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); > + > + mutex_lock(&ucr->dev_mutex); Is this normal? > + return 0; > +} > + > +static int rtsx_usb_post_reset(struct usb_interface *intf) > +{ > + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); > + > + mutex_unlock(&ucr->dev_mutex); > + return 0; > +} > + > +static struct usb_device_id rtsx_usb_usb_ids[] = { > + { USB_DEVICE(0x0BDA, 0x0129) }, > + { USB_DEVICE(0x0BDA, 0x0139) }, > + { USB_DEVICE(0x0BDA, 0x0140) }, > + { } > +}; > + > +static struct usb_driver rtsx_usb_driver = { > + .name = DRV_NAME_RTSX_USB, > + .probe = rtsx_usb_probe, > + .disconnect = rtsx_usb_disconnect, > + .suspend = rtsx_usb_suspend, > + .resume = rtsx_usb_resume, > + .reset_resume = rtsx_usb_reset_resume, > + .pre_reset = rtsx_usb_pre_reset, > + .post_reset = rtsx_usb_post_reset, > + .id_table = rtsx_usb_usb_ids, > + .supports_autosuspend = 1, > + .soft_unbind = 1, Better if you line up from the ='s I think: static struct usb_driver rtsx_usb_driver = { .name = DRV_NAME_RTSX_USB, .probe = rtsx_usb_probe, .disconnect = rtsx_usb_disconnect, .suspend = rtsx_usb_suspend, <snip> > +#include <linux/usb.h> > + > +/* related module names */ > +#define RTSX_USB_SD_CARD 0 > +#define RTSX_USB_MS_CARD 1 > + > +#define DRV_NAME_RTSX_USB "rtsx_usb" > +#define DRV_NAME_RTSX_USB_SDMMC "rtsx_usb_sdmmc" > +#define DRV_NAME_RTSX_USB_MS "rtsx_usb_ms" Can you just put the names in the correct places please? > +/* endpoint numbers */ > +#define EP_BULK_OUT 1 > +#define EP_BULK_IN 2 > +#define EP_INTR_IN 3 I assume these aren't defined anywhere else? > +/* data structures */ > +struct rtsx_ucr { > + u16 vendor_id; > + u16 product_id; > + > + int package; > +#define QFN24 0 > +#define LQFP48 1 > +#define CHECK_PKG(ucr, pkg) ((ucr)->package == (pkg)) Please remove this from the struct. > + u8 ic_version; > + u8 is_rts5179; bool > + unsigned int cur_clk; > + > + u8 *cmd_buf; > + unsigned int cmd_idx; > + u8 *rsp_buf; > + > + struct usb_device *pusb_dev; > + struct usb_interface *pusb_intf; > + struct usb_sg_request current_sg; > + unsigned char *iobuf; > + dma_addr_t iobuf_dma; > + > + struct timer_list sg_timer; > + struct mutex dev_mutex; > +}; <snip> > +static inline void rtsx_usb_init_cmd(struct rtsx_ucr *ucr) > +{ > + ucr->cmd_idx = 0; > + ucr->cmd_buf[0] = 'R'; > + ucr->cmd_buf[1] = 'T'; > + ucr->cmd_buf[2] = 'C'; > + ucr->cmd_buf[3] = 'R'; > + ucr->cmd_buf[PACKET_TYPE] = BATCH_CMD; > +} Now you have this same hunk of code 3 times in the same patch. <snip> -- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/