From owner-svn-src-all@freebsd.org Mon Jan 9 01:15:19 2017 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id BDA72CA5AA7; Mon, 9 Jan 2017 01:15:19 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 9817D118A; Mon, 9 Jan 2017 01:15:19 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v091FI7j089712; Mon, 9 Jan 2017 01:15:18 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v091FIcu089710; Mon, 9 Jan 2017 01:15:18 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201701090115.v091FIcu089710@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Mon, 9 Jan 2017 01:15:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r311727 - head/sys/dev/sdhci X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Jan 2017 01:15:19 -0000 Author: ian Date: Mon Jan 9 01:15:18 2017 New Revision: 311727 URL: https://svnweb.freebsd.org/changeset/base/311727 Log: Add support for non-removable media, and a quirk to use polling to detect card insert/remove events on controllers that don't implement the insert and remove interrupts. Bridge drivers can set a new slot option, SDHCI_NON_REMOVABLE, to indicate non-removable media (such as eMMC). The sdhci driver will not enable insert/remove interrupts, and sdhci_generic_get_card_present() will always return true. Bridge drivers can set a new quirk, SDHCI_QUIRK_POLL_CARD_PRESENT, and the sdhci driver will not enable insert/remove interrupts, and instead will use a callout to poll the card-present status at 5 Hz. For bridge drivers that get notified of card insert/remove via gpio interrupts, there is a new sdhci_handle_card_present() function they can call from the gpio interrupt handler to inform the sdhci code of the event. In addition to adding these new features, the existing code to debounce card insertions was updated to use taskqueue_enqueue_timeout() instead of scheduling a callout to do the taskqueue_enqueue(). There is also now a comment explaining that insertion-debounce is what's going on -- it took me a long time to realize that's what the old sdhci_card_delay() routine was really doing. There is no functional difference between the old and new debounce code (I hope!). Modified: head/sys/dev/sdhci/sdhci.c head/sys/dev/sdhci/sdhci.h Modified: head/sys/dev/sdhci/sdhci.c ============================================================================== --- head/sys/dev/sdhci/sdhci.c Mon Jan 9 01:12:34 2017 (r311726) +++ head/sys/dev/sdhci/sdhci.c Mon Jan 9 01:15:18 2017 (r311727) @@ -73,6 +73,7 @@ static void sdhci_set_clock(struct sdhci static void sdhci_start(struct sdhci_slot *slot); static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data); +static void sdhci_card_poll(void *); static void sdhci_card_task(void *, int); /* helper routines */ @@ -89,6 +90,9 @@ static void sdhci_card_task(void *, int) #define SDHCI_200_MAX_DIVIDER 256 #define SDHCI_300_MAX_DIVIDER 2046 +#define SDHCI_CARD_PRESENT_TICKS (hz / 5) +#define SDHCI_INSERT_DELAY_TICKS (hz / 2) + /* * Broadcom BCM577xx Controller Constants */ @@ -229,10 +233,15 @@ sdhci_init(struct sdhci_slot *slot) slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | SDHCI_INT_ACMD12ERR; + + if (!(slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) && + !(slot->opt & SDHCI_NON_REMOVABLE)) { + slot->intmask |= SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT; + } + WR4(slot, SDHCI_INT_ENABLE, slot->intmask); WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } @@ -474,14 +483,6 @@ sdhci_transfer_pio(struct sdhci_slot *sl } } -static void -sdhci_card_delay(void *arg) -{ - struct sdhci_slot *slot = arg; - - taskqueue_enqueue(taskqueue_swi_giant, &slot->card_task); -} - static void sdhci_card_task(void *arg, int pending) { @@ -491,6 +492,8 @@ sdhci_card_task(void *arg, int pending) if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) { if (slot->dev == NULL) { /* If card is present - attach mmc bus. */ + if (bootverbose || sdhci_debug) + slot_printf(slot, "Card inserted\n"); slot->dev = device_add_child(slot->bus, "mmc", -1); device_set_ivars(slot->dev, slot); SDHCI_UNLOCK(slot); @@ -500,6 +503,8 @@ sdhci_card_task(void *arg, int pending) } else { if (slot->dev != NULL) { /* If no card present - detach mmc bus. */ + if (bootverbose || sdhci_debug) + slot_printf(slot, "Card removed\n"); device_t d = slot->dev; slot->dev = NULL; SDHCI_UNLOCK(slot); @@ -509,6 +514,44 @@ sdhci_card_task(void *arg, int pending) } } +void +sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present) +{ + bool was_present; + + /* + * If there was no card and now there is one, schedule the task to + * create the child device after a short delay. The delay is to + * debounce the card insert (sometimes the card detect pin stabilizes + * before the other pins have made good contact). + * + * If there was a card present and now it's gone, immediately schedule + * the task to delete the child device. No debouncing -- gone is gone, + * because once power is removed, a full card re-init is needed, and + * that happens by deleting and recreating the child device. + */ + SDHCI_LOCK(slot); + was_present = slot->dev != NULL; + if (!was_present && is_present) { + taskqueue_enqueue_timeout(taskqueue_swi_giant, + &slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS); + } else if (was_present && !is_present) { + taskqueue_enqueue(taskqueue_swi_giant, &slot->card_task); + } + SDHCI_UNLOCK(slot); +} + +static void +sdhci_card_poll(void *arg) +{ + struct sdhci_slot *slot = arg; + + sdhci_handle_card_present(slot, + SDHCI_GET_CARD_PRESENT(slot->bus, slot)); + callout_reset(&slot->card_poll_callout, SDHCI_CARD_PRESENT_TICKS, + sdhci_card_poll, slot); +} + int sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) { @@ -652,9 +695,17 @@ sdhci_init_slot(device_t dev, struct sdh "timeout", CTLFLAG_RW, &slot->timeout, 0, "Maximum timeout for SDHCI transfers (in secs)"); TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot); - callout_init(&slot->card_callout, 1); + TIMEOUT_TASK_INIT(taskqueue_swi_giant, &slot->card_delayed_task, 0, + sdhci_card_task, slot); + callout_init(&slot->card_poll_callout, 1); callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0); + if ((slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) && + !(slot->opt & SDHCI_NON_REMOVABLE)) { + callout_reset(&slot->card_poll_callout, + SDHCI_CARD_PRESENT_TICKS, sdhci_card_poll, slot); + } + return (0); } @@ -670,8 +721,9 @@ sdhci_cleanup_slot(struct sdhci_slot *sl device_t d; callout_drain(&slot->timeout_callout); - callout_drain(&slot->card_callout); + callout_drain(&slot->card_poll_callout); taskqueue_drain(taskqueue_swi_giant, &slot->card_task); + taskqueue_drain_timeout(taskqueue_swi_giant, &slot->card_delayed_task); SDHCI_LOCK(slot); d = slot->dev; @@ -721,6 +773,9 @@ bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot) { + if (slot->opt & SDHCI_NON_REMOVABLE) + return true; + return (RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); } @@ -1326,7 +1381,7 @@ sdhci_generic_intr(struct sdhci_slot *sl /* Handle card presence interrupts. */ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - present = SDHCI_GET_CARD_PRESENT(slot->bus, slot); + present = (intmask & SDHCI_INT_CARD_INSERT) != 0; slot->intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); slot->intmask |= present ? SDHCI_INT_CARD_REMOVE : @@ -1335,20 +1390,7 @@ sdhci_generic_intr(struct sdhci_slot *sl WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask); WR4(slot, SDHCI_INT_STATUS, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)); - - if (intmask & SDHCI_INT_CARD_REMOVE) { - if (bootverbose || sdhci_debug) - slot_printf(slot, "Card removed\n"); - callout_stop(&slot->card_callout); - taskqueue_enqueue(taskqueue_swi_giant, - &slot->card_task); - } - if (intmask & SDHCI_INT_CARD_INSERT) { - if (bootverbose || sdhci_debug) - slot_printf(slot, "Card inserted\n"); - callout_reset(&slot->card_callout, hz / 2, - sdhci_card_delay, slot); - } + sdhci_handle_card_present(slot, present); intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); } /* Handle command interrupts. */ Modified: head/sys/dev/sdhci/sdhci.h ============================================================================== --- head/sys/dev/sdhci/sdhci.h Mon Jan 9 01:12:34 2017 (r311726) +++ head/sys/dev/sdhci/sdhci.h Mon Jan 9 01:15:18 2017 (r311727) @@ -65,6 +65,8 @@ #define SDHCI_QUIRK_DONT_SET_HISPD_BIT (1<<15) /* Alternate clock source is required when supplying a 400 KHz clock. */ #define SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC (1<<16) +/* Card insert/remove interrupts don't work, polling required. */ +#define SDHCI_QUIRK_POLL_CARD_PRESENT (1<<17) /* * Controller registers @@ -273,8 +275,9 @@ struct sdhci_slot { device_t dev; /* Slot device */ u_char num; /* Slot number */ u_char opt; /* Slot options */ -#define SDHCI_HAVE_DMA 1 -#define SDHCI_PLATFORM_TRANSFER 2 +#define SDHCI_HAVE_DMA 0x01 +#define SDHCI_PLATFORM_TRANSFER 0x02 +#define SDHCI_NON_REMOVABLE 0x04 u_char version; int timeout; /* Transfer timeout */ uint32_t max_clk; /* Max possible freq */ @@ -284,7 +287,9 @@ struct sdhci_slot { u_char *dmamem; bus_addr_t paddr; /* DMA buffer address */ struct task card_task; /* Card presence check task */ - struct callout card_callout; /* Card insert delay callout */ + struct timeout_task + card_delayed_task;/* Card insert delayed task */ + struct callout card_poll_callout;/* Card present polling callout */ struct callout timeout_callout;/* Card command/data response timeout */ struct mmc_host host; /* Host parameters */ struct mmc_request *req; /* Current request */ @@ -323,5 +328,6 @@ int sdhci_generic_release_host(device_t void sdhci_generic_intr(struct sdhci_slot *slot); uint32_t sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot); bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot); +void sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present); #endif /* __SDHCI_H__ */