11.1Sbrad/*	$NetBSD: umcpmio_spi.c,v 1.1 2025/11/29 18:39:14 brad Exp $	*/
21.1Sbrad
31.1Sbrad/*
41.1Sbrad * Copyright (c) 2025 Brad Spencer <brad@anduin.eldar.org>
51.1Sbrad *
61.1Sbrad * Permission to use, copy, modify, and distribute this software for any
71.1Sbrad * purpose with or without fee is hereby granted, provided that the above
81.1Sbrad * copyright notice and this permission notice appear in all copies.
91.1Sbrad *
101.1Sbrad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111.1Sbrad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121.1Sbrad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131.1Sbrad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141.1Sbrad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151.1Sbrad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161.1Sbrad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171.1Sbrad */
181.1Sbrad
191.1Sbrad#include <sys/cdefs.h>
201.1Sbrad__KERNEL_RCSID(0, "$NetBSD: umcpmio_spi.c,v 1.1 2025/11/29 18:39:14 brad Exp $");
211.1Sbrad
221.1Sbrad#ifdef _KERNEL_OPT
231.1Sbrad#include "opt_usb.h"
241.1Sbrad#endif
251.1Sbrad
261.1Sbrad#include <sys/param.h>
271.1Sbrad#include <sys/types.h>
281.1Sbrad
291.1Sbrad#include <dev/spi/spivar.h>
301.1Sbrad
311.1Sbrad#include <dev/usb/umcpmio.h>
321.1Sbrad#include <dev/usb/umcpmio_hid_reports.h>
331.1Sbrad#include <dev/usb/umcpmio_transport.h>
341.1Sbrad#include <dev/usb/umcpmio_spi.h>
351.1Sbrad#include <dev/usb/umcpmio_gpio.h>
361.1Sbrad#include <dev/usb/umcpmio_subr.h>
371.1Sbrad
381.1Sbrad#define UMCPMIO_DEBUG 1
391.1Sbrad#ifdef UMCPMIO_DEBUG
401.1Sbrad#define DPRINTF(x)	do { if (umcpmiodebug) printf x; } while (0)
411.1Sbrad#define DPRINTFN(n, x)	do { if (umcpmiodebug > (n)) printf x; } while (0)
421.1Sbradextern int umcpmiodebug;
431.1Sbrad#else
441.1Sbrad#define DPRINTF(x)	__nothing
451.1Sbrad#define DPRINTFN(n, x)	__nothing
461.1Sbrad#endif
471.1Sbrad
481.1Sbrad/* Stuff required for the SPI part of the MCP2210 */
491.1Sbrad
501.1Sbradstatic int
511.1Sbradmcp2210_get_spi_sram(struct umcpmio_softc *sc,
521.1Sbrad    struct mcp2210_get_spi_sram_res *res)
531.1Sbrad{
541.1Sbrad	struct mcp2210_get_spi_sram_req req;
551.1Sbrad	int err = 0;
561.1Sbrad
571.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
581.1Sbrad	req.cmd = MCP2210_CMD_GET_SPI_SRAM;
591.1Sbrad
601.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
611.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
621.1Sbrad	    "mcp2210_get_spi_sram req");
631.1Sbrad
641.1Sbrad	mutex_enter(&sc->sc_action_mutex);
651.1Sbrad	err = umcpmio_send_report(sc,
661.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
671.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
681.1Sbrad	mutex_exit(&sc->sc_action_mutex);
691.1Sbrad
701.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
711.1Sbrad	    (uint8_t *) res, MCP2210_RES_BUFFER_SIZE,
721.1Sbrad	    "mcp2210_get_spi_sram res");
731.1Sbrad
741.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res->completion);
751.1Sbrad
761.1Sbrad	return err;
771.1Sbrad}
781.1Sbrad
791.1Sbradstatic int
801.1Sbradmcp2210_set_spi_sram(struct umcpmio_softc *sc,
811.1Sbrad    struct mcp2210_set_spi_sram_req *req, struct mcp2210_set_spi_sram_res *res)
821.1Sbrad{
831.1Sbrad	int err = 0;
841.1Sbrad
851.1Sbrad	req->cmd = MCP2210_CMD_SET_SPI_SRAM;
861.1Sbrad
871.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
881.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
891.1Sbrad	    "mcp2210_set_spi_sram req");
901.1Sbrad
911.1Sbrad	mutex_enter(&sc->sc_action_mutex);
921.1Sbrad	err = umcpmio_send_report(sc,
931.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
941.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
951.1Sbrad	mutex_exit(&sc->sc_action_mutex);
961.1Sbrad
971.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
981.1Sbrad	    (uint8_t *) res, MCP2210_RES_BUFFER_SIZE,
991.1Sbrad	    "mcp2210_set_spi_sram res");
1001.1Sbrad
1011.1Sbrad	err = mcp2210_decode_errors(req->cmd, err, res->completion);
1021.1Sbrad
1031.1Sbrad	return err;
1041.1Sbrad}
1051.1Sbrad
1061.1Sbradstatic int
1071.1Sbradmcp2210_do_spi_transfer(struct umcpmio_softc *sc,
1081.1Sbrad    struct mcp2210_spi_transfer_req *req, struct mcp2210_spi_transfer_res *res)
1091.1Sbrad{
1101.1Sbrad	int err = 0;
1111.1Sbrad
1121.1Sbrad	req->cmd = MCP2210_CMD_SPI_TRANSFER;
1131.1Sbrad
1141.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
1151.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
1161.1Sbrad	    "mcp2210_do_spi_transfer req");
1171.1Sbrad
1181.1Sbrad	mutex_enter(&sc->sc_action_mutex);
1191.1Sbrad	err = umcpmio_send_report(sc,
1201.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
1211.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
1221.1Sbrad	mutex_exit(&sc->sc_action_mutex);
1231.1Sbrad
1241.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
1251.1Sbrad	    (uint8_t *) res, MCP2210_RES_BUFFER_SIZE,
1261.1Sbrad	    "mcp2210_do_spi_transfer res");
1271.1Sbrad
1281.1Sbrad	err = mcp2210_decode_errors(req->cmd, err, res->completion);
1291.1Sbrad
1301.1Sbrad	return err;
1311.1Sbrad}
1321.1Sbrad
1331.1Sbrad/* Most configuration is defered until a tranfer actually happens.  The
1341.1Sbrad * exception is setting the pin that is to be used as a CS to ALT0.
1351.1Sbrad *
1361.1Sbrad * There is no attempt to reset the mode of the pin used for the CS,
1371.1Sbrad * as there is no concept of a session with the SPI framework.  That is,
1381.1Sbrad * you would never know when to do it.  Further, it will cause pin
1391.1Sbrad * flapping of all of the other pins if the CS is set to INPUT
1401.1Sbrad * or OUTPUT.  Of course, that will happen when the CS mode (ALT0)
1411.1Sbrad * is set, but only the first time the pin is used as a CS.
1421.1Sbrad *
1431.1Sbrad * It is adviseable to make use of the NVRAM to set up the pin mode
1441.1Sbrad * upon boot up of the chip.
1451.1Sbrad *
1461.1Sbrad */
1471.1Sbrad
1481.1Sbradstatic int
1491.1Sbradmcp2210_spi_configure(void *cookie, int slave, int mode, int speed)
1501.1Sbrad{
1511.1Sbrad	struct umcpmio_softc *sc = cookie;
1521.1Sbrad	uint8_t desired_mode = 0;
1531.1Sbrad	struct umcpmio_spi_received *r;
1541.1Sbrad	int err = 0;
1551.1Sbrad
1561.1Sbrad	if (slave >= sc->sc_spi.sct_nslaves)
1571.1Sbrad		return EINVAL;
1581.1Sbrad
1591.1Sbrad	/* The speed is in bps and the MCP2210 uses bps, but is not suppose to
1601.1Sbrad	 * be able to go below 1500 bps.  There is also a upper bound of 12Mbps,
1611.1Sbrad	 * but the chip will not do anything wrong if you try to go faster as it
1621.1Sbrad	 * will cap the speed.
1631.1Sbrad	 */
1641.1Sbrad
1651.1Sbrad	if (speed < 1500) {
1661.1Sbrad		aprint_error_dev(sc->sc_dev,
1671.1Sbrad		    "Speed %d is too low and not supported.", speed);
1681.1Sbrad		return EINVAL;
1691.1Sbrad	}
1701.1Sbrad	mutex_enter(&sc->sc_spi_mutex);
1711.1Sbrad
1721.1Sbrad	umcpmio_gpio_pin_ctl(sc, slave, GPIO_PIN_ALT0);
1731.1Sbrad
1741.1Sbrad	switch (mode) {
1751.1Sbrad	case SPI_MODE_0:
1761.1Sbrad		desired_mode = MCP2210_SPI_MODE_0;
1771.1Sbrad		break;
1781.1Sbrad	case SPI_MODE_1:
1791.1Sbrad		desired_mode = MCP2210_SPI_MODE_1;
1801.1Sbrad		break;
1811.1Sbrad	case SPI_MODE_2:
1821.1Sbrad		desired_mode = MCP2210_SPI_MODE_2;
1831.1Sbrad		break;
1841.1Sbrad	case SPI_MODE_3:
1851.1Sbrad		desired_mode = MCP2210_SPI_MODE_3;
1861.1Sbrad		break;
1871.1Sbrad	default:
1881.1Sbrad		err = EINVAL;
1891.1Sbrad	}
1901.1Sbrad
1911.1Sbrad
1921.1Sbrad	/* Remove any queued up data.  This is a choice, but it seems the
1931.1Sbrad	 * right now.  If you call configure() you are probably resetting
1941.1Sbrad	 * the SPI port.
1951.1Sbrad	 */
1961.1Sbrad
1971.1Sbrad	if (!err) {
1981.1Sbrad		sc->sc_slave_configs[slave].bit_rate = (uint32_t) speed;
1991.1Sbrad		sc->sc_slave_configs[slave].mode = desired_mode;
2001.1Sbrad
2011.1Sbrad		if (!SIMPLEQ_EMPTY(&sc->sc_received[slave])) {
2021.1Sbrad			while ((r = SIMPLEQ_FIRST(&sc->sc_received[slave])) != NULL) {
2031.1Sbrad				SIMPLEQ_REMOVE_HEAD(&sc->sc_received[slave], umcpmio_spi_received_q);
2041.1Sbrad				kmem_free(r, sizeof(struct umcpmio_spi_received));
2051.1Sbrad			}
2061.1Sbrad		}
2071.1Sbrad	}
2081.1Sbrad
2091.1Sbrad	mutex_exit(&sc->sc_spi_mutex);
2101.1Sbrad
2111.1Sbrad	return err;
2121.1Sbrad}
2131.1Sbrad
2141.1Sbrad/* Set all of the stuff about the slave device that is needed
2151.1Sbrad * to do a transfer.
2161.1Sbrad */
2171.1Sbrad
2181.1Sbradstatic int
2191.1Sbradmcp2210_spi_set_slave_config(struct umcpmio_softc *sc,
2201.1Sbrad    uint16_t max_transfer_size, int slave)
2211.1Sbrad{
2221.1Sbrad	struct mcp2210_get_spi_sram_res get_res;
2231.1Sbrad	struct mcp2210_set_spi_sram_req set_req;
2241.1Sbrad	struct mcp2210_set_spi_sram_res set_res;
2251.1Sbrad	uint16_t idle;
2261.1Sbrad	uint16_t active;
2271.1Sbrad	int err = 0;
2281.1Sbrad
2291.1Sbrad	err = mcp2210_get_spi_sram(sc, &get_res);
2301.1Sbrad	if (err)
2311.1Sbrad		goto out;
2321.1Sbrad
2331.1Sbrad	memset(&set_res, 0, MCP2210_RES_BUFFER_SIZE);
2341.1Sbrad	memcpy(&set_req, &get_res, MCP2210_REQ_BUFFER_SIZE);
2351.1Sbrad
2361.1Sbrad	/* The MCP2210 supports the notion of a high active for
2371.1Sbrad	 * the CS, but spi(4) doesn't so, just use the normal
2381.1Sbrad	 * low active behavior all of the time.
2391.1Sbrad	 *
2401.1Sbrad	 * The chip also appears to support more than one CS
2411.1Sbrad	 * being active at the same time, but that is really
2421.1Sbrad	 * an unusual desire to have.
2431.1Sbrad	 */
2441.1Sbrad
2451.1Sbrad	idle = 0xffff;
2461.1Sbrad	active = ~(1 << slave);
2471.1Sbrad
2481.1Sbrad	set_req.idle_cs_value_lsb = (uint8_t)(idle & 0xff);
2491.1Sbrad	set_req.idle_cs_value_msb = (uint8_t)(idle >> 8);
2501.1Sbrad	set_req.active_cs_value_lsb = (uint8_t)(active & 0xff);
2511.1Sbrad	set_req.active_cs_value_msb = (uint8_t)(active >> 8);
2521.1Sbrad
2531.1Sbrad	set_req.spi_mode = sc->sc_slave_configs[slave].mode;
2541.1Sbrad
2551.1Sbrad	uint32_t s = sc->sc_slave_configs[slave].bit_rate;
2561.1Sbrad	set_req.bit_rate_byte_3 = (0x000000ff & s);
2571.1Sbrad	set_req.bit_rate_byte_2 |= (0x000000ff & (s >> 8));
2581.1Sbrad	set_req.bit_rate_byte_1 |= (0x000000ff & (s >> 16));
2591.1Sbrad	set_req.bit_rate_byte_0 |= (0x000000ff & (s >> 24));
2601.1Sbrad
2611.1Sbrad	/* The MCP2210 does best when the tranfer size is set to the
2621.1Sbrad	 * same max amount that a transfer incoming might have.
2631.1Sbrad	 */
2641.1Sbrad
2651.1Sbrad	set_req.bytes_per_spi_transaction_lsb = (uint8_t)(max_transfer_size & 0xff);
2661.1Sbrad	set_req.bytes_per_spi_transaction_msb = (uint8_t)(max_transfer_size >> 8);
2671.1Sbrad
2681.1Sbrad	err = mcp2210_set_spi_sram(sc, &set_req, &set_res);
2691.1Sbrad
2701.1Sbradout:
2711.1Sbrad
2721.1Sbrad	DPRINTF(("mcp2210_spi_set_slave exit: err=%d\n", err));
2731.1Sbrad
2741.1Sbrad	return err;
2751.1Sbrad}
2761.1Sbrad
2771.1Sbrad/* Fill up the outgoing tranfer buffers.  It is generally assumed that the state
2781.1Sbrad * of the req buffer is set coming into this function.  It should either be
2791.1Sbrad * zero filled or a repeat buffer in a retry situation.
2801.1Sbrad */
2811.1Sbrad
2821.1Sbradstatic void
2831.1Sbradmcp2210_spi_tx_fill(struct umcpmio_softc *sc,
2841.1Sbrad    struct mcp2210_spi_transfer_req *req)
2851.1Sbrad{
2861.1Sbrad	struct spi_chunk *chunk = sc->sc_wchunk;
2871.1Sbrad	size_t len;
2881.1Sbrad
2891.1Sbrad	if (chunk == NULL)
2901.1Sbrad		return;
2911.1Sbrad
2921.1Sbrad	if (sc->sc_dying)
2931.1Sbrad		return;
2941.1Sbrad
2951.1Sbrad	len = MIN(MCP2210_MAX_SPI_BYTES, chunk->chunk_wresid);
2961.1Sbrad	DPRINTF(("mcp2210_spi_tx_fill: len=%zd; chunk_wresid=%d\n",
2971.1Sbrad	    len, chunk->chunk_wresid));
2981.1Sbrad	chunk->chunk_wresid -= len;
2991.1Sbrad	req->num_send_bytes += len;
3001.1Sbrad	if (chunk->chunk_wptr)
3011.1Sbrad		memcpy(&req->send_bytes[0], chunk->chunk_wptr, len);
3021.1Sbrad	DPRINTF(("mcp2210_spi_tx_fill: checking sending values..  req->num_send_bytes=%d, chunk_wresid=%d\n",
3031.1Sbrad	    req->num_send_bytes, chunk->chunk_wresid));
3041.1Sbrad	if (chunk->chunk_wresid == 0) {
3051.1Sbrad		DPRINTF(("mcp2210_spi_tx_fill: moving onto the next chunk\n"));
3061.1Sbrad		sc->sc_wchunk = sc->sc_wchunk->chunk_next;
3071.1Sbrad		if (sc->sc_wchunk)
3081.1Sbrad			DPRINTF(("mcp2210_spi_tx_fill: new chunk EXISTS\n"));
3091.1Sbrad		else
3101.1Sbrad			DPRINTF(("mcp2210_spi_tx_fill: new chunk does not EXIST, NULL\n"));
3111.1Sbrad	}
3121.1Sbrad}
3131.1Sbrad
3141.1Sbrad/* Drain the incoming buffer into the upstream structure.  If there is no
3151.1Sbrad * upstream to drain into, then queue up the result for later.
3161.1Sbrad */
3171.1Sbrad
3181.1Sbradstatic int
3191.1Sbradmcp2210_spi_rx_drain(struct umcpmio_softc *sc, int slave,
3201.1Sbrad    struct mcp2210_spi_transfer_res *res)
3211.1Sbrad{
3221.1Sbrad	struct spi_chunk *chunk = sc->sc_rchunk;
3231.1Sbrad	size_t len;
3241.1Sbrad	struct umcpmio_spi_received *r;
3251.1Sbrad	int err = 0;
3261.1Sbrad
3271.1Sbrad	if (chunk == NULL)
3281.1Sbrad		return err;
3291.1Sbrad
3301.1Sbrad	if (sc->sc_dying)
3311.1Sbrad		return err;
3321.1Sbrad
3331.1Sbrad	len = MIN(res->num_receive_bytes, chunk->chunk_rresid);
3341.1Sbrad	chunk->chunk_rresid -= len;
3351.1Sbrad	DPRINTF(("mcp2210_spi_rx_drain: ENTER: len=%zd; res->num_receive_bytes=%d; chunk_rresid=%d\n",
3361.1Sbrad	    len, res->num_receive_bytes, chunk->chunk_rresid));
3371.1Sbrad	if (chunk->chunk_rptr)
3381.1Sbrad		memcpy(chunk->chunk_rptr, &res->receive_bytes[0], len);
3391.1Sbrad	if (chunk->chunk_rresid == 0) {
3401.1Sbrad		DPRINTF(("mcp2210_spi_rx_drain: moving onto the next chunk\n"));
3411.1Sbrad		sc->sc_rchunk = sc->sc_rchunk->chunk_next;
3421.1Sbrad		if (sc->sc_rchunk)
3431.1Sbrad			DPRINTF(("mcp2210_spi_rx_drain: new chunk EXISTS\n"));
3441.1Sbrad		else
3451.1Sbrad			DPRINTF(("mcp2210_spi_rx_drain: new chunk does not EXIST, NULL\n"));
3461.1Sbrad	}
3471.1Sbrad	DPRINTF(("mcp2210_spi_rx_drain: checking received values..  res->num_received_bytes=%d\n",
3481.1Sbrad	    res->num_receive_bytes));
3491.1Sbrad	if (len < res->num_receive_bytes) {
3501.1Sbrad		DPRINTF(("mcp2210_spi_rx_drain: did not consume all that was received, Q it up....  res->num_received_bytes=%d\n",
3511.1Sbrad		    res->num_receive_bytes));
3521.1Sbrad		r = kmem_alloc(sizeof(struct umcpmio_spi_received), KM_NOSLEEP);
3531.1Sbrad		if (r != NULL) {
3541.1Sbrad			memcpy(&r->receive_bytes[0], &res->receive_bytes[len], res->num_receive_bytes - len);
3551.1Sbrad			r->num_receive_bytes = res->num_receive_bytes - len;
3561.1Sbrad			SIMPLEQ_INSERT_HEAD(&sc->sc_received[slave], r, umcpmio_spi_received_q);
3571.1Sbrad		} else {
3581.1Sbrad			err = ENOMEM;
3591.1Sbrad		}
3601.1Sbrad	}
3611.1Sbrad	return err;
3621.1Sbrad}
3631.1Sbrad
3641.1Sbrad/* Very simular to receiving from the chip buffer, except from this
3651.1Sbrad * is from the local queue.  Requeue if needed.
3661.1Sbrad */
3671.1Sbrad
3681.1Sbradstatic int
3691.1Sbradmcp2210_spi_rx_q(struct umcpmio_softc *sc, int slave,
3701.1Sbrad    struct umcpmio_spi_received *r)
3711.1Sbrad{
3721.1Sbrad	struct spi_chunk *chunk = sc->sc_rchunk;
3731.1Sbrad	size_t len;
3741.1Sbrad	struct umcpmio_spi_received *mr;
3751.1Sbrad	int err = 0;
3761.1Sbrad
3771.1Sbrad	if (chunk == NULL)
3781.1Sbrad		return err;
3791.1Sbrad
3801.1Sbrad	if (sc->sc_dying)
3811.1Sbrad		return err;
3821.1Sbrad
3831.1Sbrad	len = MIN(r->num_receive_bytes, chunk->chunk_rresid);
3841.1Sbrad	chunk->chunk_rresid -= len;
3851.1Sbrad	DPRINTF(("mcp2210_spi_rx_q: ENTER: len=%zd; r->num_receive_bytes=%d; chunk_rresid=%d\n",
3861.1Sbrad	    len, r->num_receive_bytes, chunk->chunk_rresid));
3871.1Sbrad	if (chunk->chunk_rptr)
3881.1Sbrad		memcpy(chunk->chunk_rptr, &r->receive_bytes[0], len);
3891.1Sbrad	if (chunk->chunk_rresid == 0) {
3901.1Sbrad		DPRINTF(("mcp2210_spi_rx_q: moving onto the next chunk\n"));
3911.1Sbrad		sc->sc_rchunk = sc->sc_rchunk->chunk_next;
3921.1Sbrad		if (sc->sc_rchunk)
3931.1Sbrad			DPRINTF(("mcp2210_spi_rx_q: new chunk EXISTS\n"));
3941.1Sbrad		else
3951.1Sbrad			DPRINTF(("mcp2210_spi_rx_q: new chunk does not EXIST, NULL\n"));
3961.1Sbrad	}
3971.1Sbrad	DPRINTF(("mcp2210_spi_rx_q: checking received values..  r->num_received_bytes=%d\n",
3981.1Sbrad	    r->num_receive_bytes));
3991.1Sbrad	if (len < r->num_receive_bytes) {
4001.1Sbrad		DPRINTF(("mcp2210_spi_rx_q: did not consume all that was received, Q it up... r->num_received_bytes=%d\n",
4011.1Sbrad		    r->num_receive_bytes));
4021.1Sbrad		mr = kmem_alloc(sizeof(struct umcpmio_spi_received), KM_NOSLEEP);
4031.1Sbrad		if (mr != NULL) {
4041.1Sbrad			memcpy(&mr->receive_bytes[0], &r->receive_bytes[len], r->num_receive_bytes - len);
4051.1Sbrad			mr->num_receive_bytes = r->num_receive_bytes - len;
4061.1Sbrad			SIMPLEQ_INSERT_HEAD(&sc->sc_received[slave], mr, umcpmio_spi_received_q);
4071.1Sbrad		} else {
4081.1Sbrad			err = ENOMEM;
4091.1Sbrad		}
4101.1Sbrad	}
4111.1Sbrad	return err;
4121.1Sbrad}
4131.1Sbrad
4141.1Sbrad/* Drain the chip buffers into the local queue.  No upstream involved. */
4151.1Sbrad
4161.1Sbradstatic int
4171.1Sbradmcp2210_spi_rx_drainchip(struct umcpmio_softc *sc, int slave,
4181.1Sbrad    struct mcp2210_spi_transfer_res *res)
4191.1Sbrad{
4201.1Sbrad	int err = 0;
4211.1Sbrad	struct umcpmio_spi_received *r;
4221.1Sbrad
4231.1Sbrad	r = kmem_alloc(sizeof(struct umcpmio_spi_received), KM_NOSLEEP);
4241.1Sbrad	if (r == NULL)
4251.1Sbrad		return ENOMEM;
4261.1Sbrad	memcpy(&r->receive_bytes[0], &res->receive_bytes[0], res->num_receive_bytes);
4271.1Sbrad	r->num_receive_bytes = res->num_receive_bytes;
4281.1Sbrad	SIMPLEQ_INSERT_TAIL(&sc->sc_received[slave], r, umcpmio_spi_received_q);
4291.1Sbrad
4301.1Sbrad	return err;
4311.1Sbrad}
4321.1Sbrad
4331.1Sbrad/* Service a tranfer.  We do the local queue first if there is something, then
4341.1Sbrad * send a tranfer, with an optional amount of data, and then process any
4351.1Sbrad * incoming chip data.
4361.1Sbrad */
4371.1Sbrad
4381.1Sbradstatic int
4391.1Sbradmcp2210_spi_txrx(struct umcpmio_softc *sc, int slave)
4401.1Sbrad{
4411.1Sbrad	struct mcp2210_spi_transfer_req req;
4421.1Sbrad	struct mcp2210_spi_transfer_res res;
4431.1Sbrad	struct umcpmio_spi_received *r;
4441.1Sbrad	int err = 0;
4451.1Sbrad	int wretry = sc->sc_retry_busy_write;
4461.1Sbrad
4471.1Sbrad	DPRINTF(("mcp2210_spi_txrx: ENTER\n"));
4481.1Sbrad
4491.1Sbrad	if (sc->sc_dying)
4501.1Sbrad		return err;
4511.1Sbrad
4521.1Sbrad	/* There was local queue data and an upstream to receive it */
4531.1Sbrad
4541.1Sbrad	if (!SIMPLEQ_EMPTY(&sc->sc_received[slave]) &&
4551.1Sbrad	    sc->sc_rchunk != NULL) {
4561.1Sbrad		DPRINTF(("mcp2210_spi_txrx: previous received bytes to send to upstream\n"));
4571.1Sbrad		while ((r = SIMPLEQ_FIRST(&sc->sc_received[slave])) != NULL) {
4581.1Sbrad			SIMPLEQ_REMOVE_HEAD(&sc->sc_received[slave], umcpmio_spi_received_q);
4591.1Sbrad			err = mcp2210_spi_rx_q(sc, slave, r);
4601.1Sbrad			kmem_free(r, sizeof(struct umcpmio_spi_received));
4611.1Sbrad			if (err)
4621.1Sbrad				break;
4631.1Sbrad		}
4641.1Sbrad
4651.1Sbrad		goto out;
4661.1Sbrad	}
4671.1Sbrad	if (sc->sc_dying)
4681.1Sbrad		return err;
4691.1Sbrad
4701.1Sbrad	/* Set up a transfer and fill it once */
4711.1Sbrad
4721.1Sbrad	memset(&req, 0, UMCPMIO_REQ_BUFFER_SIZE);
4731.1Sbrad
4741.1Sbrad	if (sc->sc_wchunk != NULL)
4751.1Sbrad		DPRINTF(("mcp2210_spi_txrx: filling up req for write transfer; chunk_wresid=%d\n", sc->sc_wchunk->chunk_wresid));
4761.1Sbrad	else
4771.1Sbrad		DPRINTF(("mcp2210_spi_txrx: filling up req for write transfer; wchunk is NULL\n"));
4781.1Sbrad	mcp2210_spi_tx_fill(sc, &req);
4791.1Sbrad
4801.1Sbradretry:
4811.1Sbrad
4821.1Sbrad	memset(&res, 0, UMCPMIO_RES_BUFFER_SIZE);
4831.1Sbrad
4841.1Sbrad	if (sc->sc_dying)
4851.1Sbrad		return err;
4861.1Sbrad
4871.1Sbrad	err = mcp2210_do_spi_transfer(sc, &req, &res);
4881.1Sbrad	if (err)
4891.1Sbrad		if (err != EBUSY)
4901.1Sbrad			goto out;
4911.1Sbrad
4921.1Sbrad	DPRINTF(("mcp2210_spi_txrx: did transfer res.completion=%02x ;"
4931.1Sbrad	    "res.spi_engine_status=%02x, res.num_receive_bytes=%d\n",
4941.1Sbrad	    res.completion, res.spi_engine_status, res.num_receive_bytes));
4951.1Sbrad
4961.1Sbrad	/* This error situation of MCP2210_SPI_STATUS_DATA (0x30) and
4971.1Sbrad	 * zero received data means either the chip is stuck because
4981.1Sbrad	 * upstream didn't send enough data, or the chip is working
4991.1Sbrad	 * on receiving some data.  We only let this condution go on
5001.1Sbrad	 * a certain number of times before passing the error up.
5011.1Sbrad	 */
5021.1Sbrad
5031.1Sbrad	if (!err) {
5041.1Sbrad		if (res.spi_engine_status == MCP2210_SPI_STATUS_DATA &&
5051.1Sbrad		    res.num_receive_bytes == 0) {
5061.1Sbrad			err = ECANCELED;
5071.1Sbrad		}
5081.1Sbrad	}
5091.1Sbrad
5101.1Sbrad	/* A normal busy caused by the SPI bus being busy or the USB
5111.1Sbrad	 * bus being busy.  Try the whole transfer again.
5121.1Sbrad	 */
5131.1Sbrad
5141.1Sbrad	if (err == EBUSY) {
5151.1Sbrad		DPRINTF(("mcp2210_spi_txrx: transfer is busy\n"));
5161.1Sbrad		wretry--;
5171.1Sbrad		if (wretry > 0) {
5181.1Sbrad			WAITMS(sc->sc_busy_delay);
5191.1Sbrad			goto retry;
5201.1Sbrad		} else {
5211.1Sbrad			err = EIO;
5221.1Sbrad			goto out;
5231.1Sbrad		}
5241.1Sbrad	}
5251.1Sbrad
5261.1Sbrad	/* If the chip knows that there is data coming, but has not seen
5271.1Sbrad	 * any yet you get this error.  Retry the transfer with a zero
5281.1Sbrad	 * length outgoing buffer.
5291.1Sbrad	 */
5301.1Sbrad
5311.1Sbrad	if (res.spi_engine_status == MCP2210_SPI_STATUS_NO_DATA_YET) {
5321.1Sbrad		DPRINTF(("mcp2210_spi_txrx: transfer is NO DATA YET\n"));
5331.1Sbrad		wretry--;
5341.1Sbrad		if (wretry > 0) {
5351.1Sbrad			WAITMS(sc->sc_busy_delay);
5361.1Sbrad			memset(&req, 0, UMCPMIO_REQ_BUFFER_SIZE);
5371.1Sbrad			goto retry;
5381.1Sbrad		} else {
5391.1Sbrad			err = EIO;
5401.1Sbrad			goto out;
5411.1Sbrad		}
5421.1Sbrad	}
5431.1Sbrad	DPRINTF(("mcp2210_spi_txrx: DOING RECEIVE: res.num_receive_bytes=%d\n",
5441.1Sbrad	    res.num_receive_bytes));
5451.1Sbrad
5461.1Sbrad	/* If we actually received some data, drain it somewhere */
5471.1Sbrad
5481.1Sbrad	if (res.num_receive_bytes > 0) {
5491.1Sbrad		if (sc->sc_rchunk != NULL &&
5501.1Sbrad		    sc->sc_rchunk->chunk_rptr) {
5511.1Sbrad			DPRINTF(("mcp2210_spi_txrx: send data to upstream\n"));
5521.1Sbrad			err = mcp2210_spi_rx_drain(sc, slave, &res);
5531.1Sbrad		} else {
5541.1Sbrad			DPRINTF(("mcp2210_spi_txrx: drain chip, send data to Q\n"));
5551.1Sbrad			err = mcp2210_spi_rx_drainchip(sc, slave, &res);
5561.1Sbrad		}
5571.1Sbrad	}
5581.1Sbradout:
5591.1Sbrad
5601.1Sbrad	DPRINTF(("mcp2210_spi_txrx exit.  err=%d\n", err));
5611.1Sbrad
5621.1Sbrad	return err;
5631.1Sbrad}
5641.1Sbrad
5651.1Sbrad/* Cancel a SPI transfer that might be in progress, or at
5661.1Sbrad * least the chip thinks is in progress.
5671.1Sbrad */
5681.1Sbrad
5691.1Sbradint
5701.1Sbradmcp2210_cancel_spi_transfer(struct umcpmio_softc *sc,
5711.1Sbrad    struct mcp2210_cancel_spi_res *res)
5721.1Sbrad{
5731.1Sbrad	struct mcp2210_cancel_spi_req req;
5741.1Sbrad	int err = 0;
5751.1Sbrad
5761.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
5771.1Sbrad
5781.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
5791.1Sbrad	req.cmd = MCP2210_CMD_SPI_CANCEL;
5801.1Sbrad
5811.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
5821.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
5831.1Sbrad	    "mcp2210_cancel_spi_transfer req");
5841.1Sbrad
5851.1Sbrad	err = umcpmio_send_report(sc,
5861.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
5871.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
5881.1Sbrad
5891.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
5901.1Sbrad	    (uint8_t *) res, MCP2210_RES_BUFFER_SIZE,
5911.1Sbrad	    "mcp2210_get_spi_sram res");
5921.1Sbrad
5931.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res->completion);
5941.1Sbrad
5951.1Sbrad	return err;
5961.1Sbrad}
5971.1Sbrad
5981.1Sbrad/* As long as there are outgoing chunks to send,
5991.1Sbrad * or incoming chunks to receive, poll the SPI port.
6001.1Sbrad */
6011.1Sbrad
6021.1Sbradstatic void
6031.1Sbradmcp2210_spi_poll(struct umcpmio_softc *sc,
6041.1Sbrad    int slave, int *stuckcount)
6051.1Sbrad{
6061.1Sbrad	struct spi_transfer *st;
6071.1Sbrad	struct mcp2210_cancel_spi_res cancel_res;
6081.1Sbrad	int err = 0;
6091.1Sbrad
6101.1Sbrad	err = mcp2210_spi_txrx(sc, slave);
6111.1Sbrad
6121.1Sbrad	DPRINTF(("mcp2210_spi_poll: err from transfer: err=%d\n", err));
6131.1Sbrad
6141.1Sbrad	/* Give up if we exceed the stuck count.  It is ok for this to happen
6151.1Sbrad	 * a couple of times during a transfer, but if it persists, something is
6161.1Sbrad	 * going wrong.
6171.1Sbrad	 */
6181.1Sbrad	if (err == ECANCELED) {
6191.1Sbrad		int s;
6201.1Sbrad
6211.1Sbrad		s = *stuckcount;
6221.1Sbrad		s--;
6231.1Sbrad		DPRINTF(("mcp2210_spi_poll: STUCK s=%d\n", s));
6241.1Sbrad		if (s > 0)
6251.1Sbrad			err = 0;
6261.1Sbrad		*stuckcount = s;
6271.1Sbrad	}
6281.1Sbrad
6291.1Sbrad	if (err) {
6301.1Sbrad		if (err == ECANCELED) {
6311.1Sbrad			if (sc->sc_spi_verbose)
6321.1Sbrad				device_printf(sc->sc_dev, "The SPI transfer may be stuck.  Canceling.\n");
6331.1Sbrad			mutex_enter(&sc->sc_action_mutex);
6341.1Sbrad			mcp2210_cancel_spi_transfer(sc, &cancel_res);
6351.1Sbrad			mutex_exit(&sc->sc_action_mutex);
6361.1Sbrad		}
6371.1Sbrad		sc->sc_rchunk = sc->sc_wchunk = NULL;
6381.1Sbrad	}
6391.1Sbrad
6401.1Sbrad	/* Finialize the transfer */
6411.1Sbrad
6421.1Sbrad	if (sc->sc_rchunk == NULL &&
6431.1Sbrad	    sc->sc_wchunk == NULL) {
6441.1Sbrad		st = sc->sc_transfer;
6451.1Sbrad		sc->sc_transfer = NULL;
6461.1Sbrad		KASSERT(st != NULL);
6471.1Sbrad		spi_done(st, 0);
6481.1Sbrad		sc->sc_running = false;
6491.1Sbrad	}
6501.1Sbrad	return;
6511.1Sbrad}
6521.1Sbrad
6531.1Sbrad/* Start a SPI transfer up and running */
6541.1Sbrad
6551.1Sbradstatic void
6561.1Sbradmcp2210_spi_start(struct umcpmio_softc *sc)
6571.1Sbrad{
6581.1Sbrad	struct spi_transfer *st;
6591.1Sbrad	struct spi_chunk *chunk;
6601.1Sbrad	uint16_t max_transfer_size = 0;
6611.1Sbrad	int transfer_size = 0;
6621.1Sbrad	int err = 0;
6631.1Sbrad	int stuckcount = 4; /* XXX - maybe this should be a sysctl */
6641.1Sbrad	struct mcp2210_cancel_spi_res cancel_res;
6651.1Sbrad
6661.1Sbrad	while ((st = spi_transq_first(&sc->sc_q)) != NULL) {
6671.1Sbrad		spi_transq_dequeue(&sc->sc_q);
6681.1Sbrad		KASSERT(sc->sc_transfer == NULL);
6691.1Sbrad		sc->sc_transfer = st;
6701.1Sbrad		sc->sc_rchunk = sc->sc_wchunk = st->st_chunks;
6711.1Sbrad		sc->sc_running = true;
6721.1Sbrad
6731.1Sbrad		KASSERT(st->st_slave < sc->sc_spi.sct_nslaves);
6741.1Sbrad
6751.1Sbrad		/* Figure out the maximum size for this transfer.
6761.1Sbrad		 * It doesn't matter if it is a incoming or outgoing
6771.1Sbrad		 * chunk.
6781.1Sbrad		 */
6791.1Sbrad		chunk = st->st_chunks;
6801.1Sbrad		while (chunk != NULL) {
6811.1Sbrad			if (chunk->chunk_wresid > transfer_size)
6821.1Sbrad				transfer_size = chunk->chunk_wresid;
6831.1Sbrad			if (chunk->chunk_rresid > transfer_size)
6841.1Sbrad				transfer_size = chunk->chunk_rresid;
6851.1Sbrad			chunk = chunk->chunk_next;
6861.1Sbrad		}
6871.1Sbrad
6881.1Sbrad		KASSERT(transfer_size > 0);
6891.1Sbrad
6901.1Sbrad		/* The chip can do up to 65535 bytes in a transfer.  We subtract
6911.1Sbrad		 * 1 so the slave device can send back a end byte.  There is,
6921.1Sbrad		 * apparently some was to do larger transfers, but I could
6931.1Sbrad		 * not find documentation on how to do that.
6941.1Sbrad		 */
6951.1Sbrad		if (transfer_size > 65534) {
6961.1Sbrad			if (sc->sc_spi_verbose)
6971.1Sbrad				device_printf(sc->sc_dev, "Canceling transfer.  "
6981.1Sbrad				    "The size of the requested transfer is too big.  "
6991.1Sbrad				    "transfer_size=%d\n",
7001.1Sbrad				    transfer_size);
7011.1Sbrad
7021.1Sbrad			st = sc->sc_transfer;
7031.1Sbrad			sc->sc_transfer = NULL;
7041.1Sbrad			spi_done(st, 0);
7051.1Sbrad			sc->sc_running = false;
7061.1Sbrad
7071.1Sbrad			return;
7081.1Sbrad		}
7091.1Sbrad		max_transfer_size = (uint16_t) transfer_size + 1;
7101.1Sbrad
7111.1Sbrad		DPRINTF(("mcp2210_spi_start: transfer_size=%d; max_transfer_size=%d\n", transfer_size, max_transfer_size));
7121.1Sbrad
7131.1Sbrad		/* Set all of the defered setting now that we are commited to trying
7141.1Sbrad		 * to do the transfer.
7151.1Sbrad		 */
7161.1Sbrad		err = mcp2210_spi_set_slave_config(sc, max_transfer_size, st->st_slave);
7171.1Sbrad		/* If we get EBUSY here, then it probably means that there was a incomplete
7181.1Sbrad		 * transfer.  Cancel it, and try one more time.
7191.1Sbrad		 */
7201.1Sbrad		if (err == EBUSY) {
7211.1Sbrad			mutex_enter(&sc->sc_action_mutex);
7221.1Sbrad			mcp2210_cancel_spi_transfer(sc, &cancel_res);
7231.1Sbrad			mutex_exit(&sc->sc_action_mutex);
7241.1Sbrad			err = mcp2210_spi_set_slave_config(sc, max_transfer_size, st->st_slave);
7251.1Sbrad		}
7261.1Sbrad		if (err) {
7271.1Sbrad			if (sc->sc_spi_verbose)
7281.1Sbrad				device_printf(sc->sc_dev, "Canceling transfer.  Error setting up slave config.  error=%d\n", err);
7291.1Sbrad
7301.1Sbrad			st = sc->sc_transfer;
7311.1Sbrad			sc->sc_transfer = NULL;
7321.1Sbrad			spi_done(st, 0);
7331.1Sbrad			sc->sc_running = false;
7341.1Sbrad
7351.1Sbrad			return;
7361.1Sbrad		}
7371.1Sbrad
7381.1Sbrad		for (;;) {
7391.1Sbrad			mcp2210_spi_poll(sc, st->st_slave, &stuckcount);
7401.1Sbrad			if (ISSET(st->st_flags, SPI_F_DONE))
7411.1Sbrad				break;
7421.1Sbrad			if (sc->sc_dying)
7431.1Sbrad				break;
7441.1Sbrad		}
7451.1Sbrad	}
7461.1Sbrad	sc->sc_running = false;
7471.1Sbrad
7481.1Sbrad	return;
7491.1Sbrad}
7501.1Sbrad
7511.1Sbradstatic int
7521.1Sbradmcp2210_spi_transfer(void *cookie, struct spi_transfer *st)
7531.1Sbrad{
7541.1Sbrad	struct umcpmio_softc *sc = cookie;
7551.1Sbrad
7561.1Sbrad	mutex_enter(&sc->sc_spi_mutex);
7571.1Sbrad	spi_transq_enqueue(&sc->sc_q, st);
7581.1Sbrad	if (sc->sc_running == false) {
7591.1Sbrad		mcp2210_spi_start(sc);
7601.1Sbrad	}
7611.1Sbrad	mutex_exit(&sc->sc_spi_mutex);
7621.1Sbrad
7631.1Sbrad	return 0;
7641.1Sbrad}
7651.1Sbrad
7661.1Sbradint
7671.1Sbradumcpmio_spi_attach(struct umcpmio_softc *sc)
7681.1Sbrad{
7691.1Sbrad	struct mcp2210_get_spi_sram_res get_res;
7701.1Sbrad	struct mcp2210_set_spi_sram_req set_req;
7711.1Sbrad	struct mcp2210_set_spi_sram_res set_res;
7721.1Sbrad	struct mcp2210_cancel_spi_res cancel_res;
7731.1Sbrad	int err = 0;
7741.1Sbrad
7751.1Sbrad	sc->sc_spi_verbose = true;
7761.1Sbrad	sc->sc_busy_delay = 0;
7771.1Sbrad	sc->sc_retry_busy_read = 50;
7781.1Sbrad	sc->sc_retry_busy_write = 50;
7791.1Sbrad	sc->sc_running = false;
7801.1Sbrad
7811.1Sbrad	DPRINTF(("umcpmio_spi_attach: sc->sc_chipinfo->num_spi_slaves=%d\n", sc->sc_chipinfo->num_spi_slaves));
7821.1Sbrad
7831.1Sbrad	SIMPLEQ_INIT(&sc->sc_q);
7841.1Sbrad
7851.1Sbrad	for (int i = 0; i < sc->sc_chipinfo->num_spi_slaves; i++){
7861.1Sbrad		SIMPLEQ_INIT(&sc->sc_received[i]);
7871.1Sbrad		sc->sc_slave_configs[i].bit_rate = (uint32_t) SPI_FREQ_MHz(1);
7881.1Sbrad		sc->sc_slave_configs[i].mode = MCP2210_SPI_MODE_0;
7891.1Sbrad	}
7901.1Sbrad
7911.1Sbrad	/* Set all of the active and idle CS directions here. */
7921.1Sbrad	err = mcp2210_get_spi_sram(sc, &get_res);
7931.1Sbrad	if (err) {
7941.1Sbrad		aprint_error_dev(sc->sc_dev,
7951.1Sbrad		    "%s: unable to read SPI sram: %d\n",
7961.1Sbrad		    __func__, err);
7971.1Sbrad		goto out;
7981.1Sbrad	}
7991.1Sbrad	memset(&set_res, 0, MCP2210_RES_BUFFER_SIZE);
8001.1Sbrad	memcpy(&set_req, &get_res, MCP2210_REQ_BUFFER_SIZE);
8011.1Sbrad
8021.1Sbrad	set_req.idle_cs_value_lsb = set_req.idle_cs_value_msb = 0xff;
8031.1Sbrad	set_req.active_cs_value_lsb = set_req.active_cs_value_msb = 0x00;
8041.1Sbrad
8051.1Sbrad	err = mcp2210_set_spi_sram(sc, &set_req, &set_res);
8061.1Sbrad	/* If there is a EBUSY here, then a transfer was incomplete
8071.1Sbrad	 * in the past.  That can happen on attach if the chip stays
8081.1Sbrad	 * powered up, but is removed from the USB port and
8091.1Sbrad	 * reinserted.  Cancel the transfer and try one more time.
8101.1Sbrad	 */
8111.1Sbrad	if (err == EBUSY) {
8121.1Sbrad		mutex_enter(&sc->sc_action_mutex);
8131.1Sbrad		mcp2210_cancel_spi_transfer(sc, &cancel_res);
8141.1Sbrad		mutex_exit(&sc->sc_action_mutex);
8151.1Sbrad		err = mcp2210_set_spi_sram(sc, &set_req, &set_res);
8161.1Sbrad	}
8171.1Sbrad	if (err) {
8181.1Sbrad		aprint_error_dev(sc->sc_dev,
8191.1Sbrad		    "%s: unable to set SPI sram: %d\n",
8201.1Sbrad		    __func__, err);
8211.1Sbrad		goto out;
8221.1Sbrad	}
8231.1Sbrad	sc->sc_spi.sct_cookie = sc;
8241.1Sbrad	sc->sc_spi.sct_configure = mcp2210_spi_configure;
8251.1Sbrad	sc->sc_spi.sct_transfer = mcp2210_spi_transfer;
8261.1Sbrad	sc->sc_spi.sct_nslaves = sc->sc_chipinfo->num_spi_slaves;
8271.1Sbrad
8281.1Sbrad	spibus_attach(sc->sc_dev, &sc->sc_spi);
8291.1Sbrad
8301.1Sbradout:
8311.1Sbrad
8321.1Sbrad	return err;
8331.1Sbrad}
834