From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- drivers/mmc/host/ushc.c | 568 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 drivers/mmc/host/ushc.c (limited to 'drivers/mmc/host/ushc.c') diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c new file mode 100644 index 000000000..9a6358fd9 --- /dev/null +++ b/drivers/mmc/host/ushc.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB SD Host Controller (USHC) controller driver. + * + * Copyright (C) 2010 Cambridge Silicon Radio Ltd. + * + * Notes: + * - Only version 2 devices are supported. + * - Version 2 devices only support SDIO cards/devices (R2 response is + * unsupported). + * + * References: + * [USHC] USB SD Host Controller specification (CS-118793-SP) + */ +#include +#include +#include +#include +#include +#include + +enum ushc_request { + USHC_GET_CAPS = 0x00, + USHC_HOST_CTRL = 0x01, + USHC_PWR_CTRL = 0x02, + USHC_CLK_FREQ = 0x03, + USHC_EXEC_CMD = 0x04, + USHC_READ_RESP = 0x05, + USHC_RESET = 0x06, +}; + +enum ushc_request_type { + USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +}; + +#define USHC_GET_CAPS_VERSION_MASK 0xff +#define USHC_GET_CAPS_3V3 (1 << 8) +#define USHC_GET_CAPS_3V0 (1 << 9) +#define USHC_GET_CAPS_1V8 (1 << 10) +#define USHC_GET_CAPS_HIGH_SPD (1 << 16) + +#define USHC_HOST_CTRL_4BIT (1 << 1) +#define USHC_HOST_CTRL_HIGH_SPD (1 << 0) + +#define USHC_PWR_CTRL_OFF 0x00 +#define USHC_PWR_CTRL_3V3 0x01 +#define USHC_PWR_CTRL_3V0 0x02 +#define USHC_PWR_CTRL_1V8 0x03 + +#define USHC_READ_RESP_BUSY (1 << 4) +#define USHC_READ_RESP_ERR_TIMEOUT (1 << 3) +#define USHC_READ_RESP_ERR_CRC (1 << 2) +#define USHC_READ_RESP_ERR_DAT (1 << 1) +#define USHC_READ_RESP_ERR_CMD (1 << 0) +#define USHC_READ_RESP_ERR_MASK 0x0f + +struct ushc_cbw { + __u8 signature; + __u8 cmd_idx; + __le16 block_size; + __le32 arg; +} __attribute__((packed)); + +#define USHC_CBW_SIGNATURE 'C' + +struct ushc_csw { + __u8 signature; + __u8 status; + __le32 response; +} __attribute__((packed)); + +#define USHC_CSW_SIGNATURE 'S' + +struct ushc_int_data { + u8 status; + u8 reserved[3]; +}; + +#define USHC_INT_STATUS_SDIO_INT (1 << 1) +#define USHC_INT_STATUS_CARD_PRESENT (1 << 0) + + +struct ushc_data { + struct usb_device *usb_dev; + struct mmc_host *mmc; + + struct urb *int_urb; + struct ushc_int_data *int_data; + + struct urb *cbw_urb; + struct ushc_cbw *cbw; + + struct urb *data_urb; + + struct urb *csw_urb; + struct ushc_csw *csw; + + spinlock_t lock; + struct mmc_request *current_req; + u32 caps; + u16 host_ctrl; + unsigned long flags; + u8 last_status; + int clock_freq; +}; + +#define DISCONNECTED 0 +#define INT_EN 1 +#define IGNORE_NEXT_INT 2 + +static void data_callback(struct urb *urb); + +static int ushc_hw_reset(struct ushc_data *ushc) +{ + return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_RESET, USHC_RESET_TYPE, + 0, 0, NULL, 0, 100); +} + +static int ushc_hw_get_caps(struct ushc_data *ushc) +{ + int ret; + int version; + + ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0), + USHC_GET_CAPS, USHC_GET_CAPS_TYPE, + 0, 0, &ushc->caps, sizeof(ushc->caps), 100); + if (ret < 0) + return ret; + + ushc->caps = le32_to_cpu(ushc->caps); + + version = ushc->caps & USHC_GET_CAPS_VERSION_MASK; + if (version != 0x02) { + dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version); + return -EINVAL; + } + + return 0; +} + +static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val) +{ + u16 host_ctrl; + int ret; + + host_ctrl = (ushc->host_ctrl & ~mask) | val; + ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE, + host_ctrl, 0, NULL, 0, 100); + if (ret < 0) + return ret; + ushc->host_ctrl = host_ctrl; + return 0; +} + +static void int_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + u8 status, last_status; + + if (urb->status < 0) + return; + + status = ushc->int_data->status; + last_status = ushc->last_status; + ushc->last_status = status; + + /* + * Ignore the card interrupt status on interrupt transfers that + * were submitted while card interrupts where disabled. + * + * This avoid occasional spurious interrupts when enabling + * interrupts immediately after clearing the source on the card. + */ + + if (!test_and_clear_bit(IGNORE_NEXT_INT, &ushc->flags) + && test_bit(INT_EN, &ushc->flags) + && status & USHC_INT_STATUS_SDIO_INT) { + mmc_signal_sdio_irq(ushc->mmc); + } + + if ((status ^ last_status) & USHC_INT_STATUS_CARD_PRESENT) + mmc_detect_change(ushc->mmc, msecs_to_jiffies(100)); + + if (!test_bit(INT_EN, &ushc->flags)) + set_bit(IGNORE_NEXT_INT, &ushc->flags); + usb_submit_urb(ushc->int_urb, GFP_ATOMIC); +} + +static void cbw_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + + if (urb->status != 0) { + usb_unlink_urb(ushc->data_urb); + usb_unlink_urb(ushc->csw_urb); + } +} + +static void data_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + + if (urb->status != 0) + usb_unlink_urb(ushc->csw_urb); +} + +static void csw_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + struct mmc_request *req = ushc->current_req; + int status; + + status = ushc->csw->status; + + if (urb->status != 0) { + req->cmd->error = urb->status; + } else if (status & USHC_READ_RESP_ERR_CMD) { + if (status & USHC_READ_RESP_ERR_CRC) + req->cmd->error = -EIO; + else + req->cmd->error = -ETIMEDOUT; + } + if (req->data) { + if (status & USHC_READ_RESP_ERR_DAT) { + if (status & USHC_READ_RESP_ERR_CRC) + req->data->error = -EIO; + else + req->data->error = -ETIMEDOUT; + req->data->bytes_xfered = 0; + } else { + req->data->bytes_xfered = req->data->blksz * req->data->blocks; + } + } + + req->cmd->resp[0] = le32_to_cpu(ushc->csw->response); + + mmc_request_done(ushc->mmc, req); +} + +static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct ushc_data *ushc = mmc_priv(mmc); + int ret; + unsigned long flags; + + spin_lock_irqsave(&ushc->lock, flags); + + if (test_bit(DISCONNECTED, &ushc->flags)) { + ret = -ENODEV; + goto out; + } + + /* Version 2 firmware doesn't support the R2 response format. */ + if (req->cmd->flags & MMC_RSP_136) { + ret = -EINVAL; + goto out; + } + + /* The Astoria's data FIFOs don't work with clock speeds < 5MHz so + limit commands with data to 6MHz or more. */ + if (req->data && ushc->clock_freq < 6000000) { + ret = -EINVAL; + goto out; + } + + ushc->current_req = req; + + /* Start cmd with CBW. */ + ushc->cbw->cmd_idx = cpu_to_le16(req->cmd->opcode); + if (req->data) + ushc->cbw->block_size = cpu_to_le16(req->data->blksz); + else + ushc->cbw->block_size = 0; + ushc->cbw->arg = cpu_to_le32(req->cmd->arg); + + ret = usb_submit_urb(ushc->cbw_urb, GFP_ATOMIC); + if (ret < 0) + goto out; + + /* Submit data (if any). */ + if (req->data) { + struct mmc_data *data = req->data; + int pipe; + + if (data->flags & MMC_DATA_READ) + pipe = usb_rcvbulkpipe(ushc->usb_dev, 6); + else + pipe = usb_sndbulkpipe(ushc->usb_dev, 2); + + usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe, + NULL, data->sg->length, + data_callback, ushc); + ushc->data_urb->num_sgs = 1; + ushc->data_urb->sg = data->sg; + ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); + if (ret < 0) + goto out; + } + + /* Submit CSW. */ + ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC); + +out: + spin_unlock_irqrestore(&ushc->lock, flags); + if (ret < 0) { + usb_unlink_urb(ushc->cbw_urb); + usb_unlink_urb(ushc->data_urb); + req->cmd->error = ret; + mmc_request_done(mmc, req); + } +} + +static int ushc_set_power(struct ushc_data *ushc, unsigned char power_mode) +{ + u16 voltage; + + switch (power_mode) { + case MMC_POWER_OFF: + voltage = USHC_PWR_CTRL_OFF; + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + voltage = USHC_PWR_CTRL_3V3; + break; + default: + return -EINVAL; + } + + return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_PWR_CTRL, USHC_PWR_CTRL_TYPE, + voltage, 0, NULL, 0, 100); +} + +static int ushc_set_bus_width(struct ushc_data *ushc, int bus_width) +{ + return ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_4BIT, + bus_width == 4 ? USHC_HOST_CTRL_4BIT : 0); +} + +static int ushc_set_bus_freq(struct ushc_data *ushc, int clk, bool enable_hs) +{ + int ret; + + /* Hardware can't detect interrupts while the clock is off. */ + if (clk == 0) + clk = 400000; + + ret = ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_HIGH_SPD, + enable_hs ? USHC_HOST_CTRL_HIGH_SPD : 0); + if (ret < 0) + return ret; + + ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_CLK_FREQ, USHC_CLK_FREQ_TYPE, + clk & 0xffff, (clk >> 16) & 0xffff, NULL, 0, 100); + if (ret < 0) + return ret; + + ushc->clock_freq = clk; + return 0; +} + +static void ushc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + ushc_set_power(ushc, ios->power_mode); + ushc_set_bus_width(ushc, 1 << ios->bus_width); + ushc_set_bus_freq(ushc, ios->clock, ios->timing == MMC_TIMING_SD_HS); +} + +static int ushc_get_cd(struct mmc_host *mmc) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + return !!(ushc->last_status & USHC_INT_STATUS_CARD_PRESENT); +} + +static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + if (enable) + set_bit(INT_EN, &ushc->flags); + else + clear_bit(INT_EN, &ushc->flags); +} + +static void ushc_clean_up(struct ushc_data *ushc) +{ + usb_free_urb(ushc->int_urb); + usb_free_urb(ushc->csw_urb); + usb_free_urb(ushc->data_urb); + usb_free_urb(ushc->cbw_urb); + + kfree(ushc->int_data); + kfree(ushc->cbw); + kfree(ushc->csw); + + mmc_free_host(ushc->mmc); +} + +static const struct mmc_host_ops ushc_ops = { + .request = ushc_request, + .set_ios = ushc_set_ios, + .get_cd = ushc_get_cd, + .enable_sdio_irq = ushc_enable_sdio_irq, +}; + +static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct mmc_host *mmc; + struct ushc_data *ushc; + int ret; + + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + + mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); + if (mmc == NULL) + return -ENOMEM; + ushc = mmc_priv(mmc); + usb_set_intfdata(intf, ushc); + + ushc->usb_dev = usb_dev; + ushc->mmc = mmc; + + spin_lock_init(&ushc->lock); + + ret = ushc_hw_reset(ushc); + if (ret < 0) + goto err; + + /* Read capabilities. */ + ret = ushc_hw_get_caps(ushc); + if (ret < 0) + goto err; + + mmc->ops = &ushc_ops; + + mmc->f_min = 400000; + mmc->f_max = 50000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; + mmc->caps |= (ushc->caps & USHC_GET_CAPS_HIGH_SPD) ? MMC_CAP_SD_HIGHSPEED : 0; + + mmc->max_seg_size = 512*511; + mmc->max_segs = 1; + mmc->max_req_size = 512*511; + mmc->max_blk_size = 512; + mmc->max_blk_count = 511; + + ushc->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->int_urb == NULL) { + ret = -ENOMEM; + goto err; + } + ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL); + if (ushc->int_data == NULL) { + ret = -ENOMEM; + goto err; + } + usb_fill_int_urb(ushc->int_urb, ushc->usb_dev, + usb_rcvintpipe(usb_dev, + intf->cur_altsetting->endpoint[0].desc.bEndpointAddress), + ushc->int_data, sizeof(struct ushc_int_data), + int_callback, ushc, + intf->cur_altsetting->endpoint[0].desc.bInterval); + + ushc->cbw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->cbw_urb == NULL) { + ret = -ENOMEM; + goto err; + } + ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); + if (ushc->cbw == NULL) { + ret = -ENOMEM; + goto err; + } + ushc->cbw->signature = USHC_CBW_SIGNATURE; + + usb_fill_bulk_urb(ushc->cbw_urb, ushc->usb_dev, usb_sndbulkpipe(usb_dev, 2), + ushc->cbw, sizeof(struct ushc_cbw), + cbw_callback, ushc); + + ushc->data_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->data_urb == NULL) { + ret = -ENOMEM; + goto err; + } + + ushc->csw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->csw_urb == NULL) { + ret = -ENOMEM; + goto err; + } + ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL); + if (ushc->csw == NULL) { + ret = -ENOMEM; + goto err; + } + usb_fill_bulk_urb(ushc->csw_urb, ushc->usb_dev, usb_rcvbulkpipe(usb_dev, 6), + ushc->csw, sizeof(struct ushc_csw), + csw_callback, ushc); + + ret = mmc_add_host(ushc->mmc); + if (ret) + goto err; + + ret = usb_submit_urb(ushc->int_urb, GFP_KERNEL); + if (ret < 0) { + mmc_remove_host(ushc->mmc); + goto err; + } + + return 0; + +err: + ushc_clean_up(ushc); + return ret; +} + +static void ushc_disconnect(struct usb_interface *intf) +{ + struct ushc_data *ushc = usb_get_intfdata(intf); + + spin_lock_irq(&ushc->lock); + set_bit(DISCONNECTED, &ushc->flags); + spin_unlock_irq(&ushc->lock); + + usb_kill_urb(ushc->int_urb); + usb_kill_urb(ushc->cbw_urb); + usb_kill_urb(ushc->data_urb); + usb_kill_urb(ushc->csw_urb); + + mmc_remove_host(ushc->mmc); + + ushc_clean_up(ushc); +} + +static struct usb_device_id ushc_id_table[] = { + /* CSR USB SD Host Controller */ + { USB_DEVICE(0x0a12, 0x5d10) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, ushc_id_table); + +static struct usb_driver ushc_driver = { + .name = "ushc", + .id_table = ushc_id_table, + .probe = ushc_probe, + .disconnect = ushc_disconnect, +}; + +module_usb_driver(ushc_driver); + +MODULE_DESCRIPTION("USB SD Host Controller driver"); +MODULE_AUTHOR("David Vrabel "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3