https://github.com/torvalds/linux
Revision 6f0306d1bfbc4d3e3fcfb949ee2c4af21071920a authored by Linus Torvalds on 30 August 2020, 17:51:03 UTC, committed by Linus Torvalds on 30 August 2020, 17:51:03 UTC
Pull USB fixes from Greg KH:
 "Let's try this again...  Here are some USB fixes for 5.9-rc3.

  This differs from the previous pull request for this release in that
  the usb gadget patch now does not break some systems, and actually
  does what it was intended to do. Many thanks to Marek Szyprowski for
  quickly noticing and testing the patch from Andy Shevchenko to resolve
  this issue.

  Additionally, some more new USB quirks have been added to get some new
  devices to work properly based on user reports.

  Other than that, the patches are all here, and they contain:

   - usb gadget driver fixes

   - xhci driver fixes

   - typec fixes

   - new quirks and ids

   - fixes for USB patches that went into 5.9-rc1.

  All of these have been tested in linux-next with no reported issues"

* tag 'usb-5.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (33 commits)
  usb: storage: Add unusual_uas entry for Sony PSZ drives
  USB: Ignore UAS for JMicron JMS567 ATA/ATAPI Bridge
  usb: host: ohci-exynos: Fix error handling in exynos_ohci_probe()
  USB: gadget: u_f: Unbreak offset calculation in VLAs
  USB: quirks: Ignore duplicate endpoint on Sound Devices MixPre-D
  usb: typec: tcpm: Fix Fix source hard reset response for TDA 2.3.1.1 and TDA 2.3.1.2 failures
  USB: PHY: JZ4770: Fix static checker warning.
  USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()
  USB: gadget: u_f: add overflow checks to VLA macros
  xhci: Always restore EP_SOFT_CLEAR_TOGGLE even if ep reset failed
  xhci: Do warm-reset when both CAS and XDEV_RESUME are set
  usb: host: xhci: fix ep context print mismatch in debugfs
  usb: uas: Add quirk for PNY Pro Elite
  tools: usb: move to tools buildsystem
  USB: Fix device driver race
  USB: Also match device drivers using the ->match vfunc
  usb: host: xhci-tegra: fix tegra_xusb_get_phy()
  usb: host: xhci-tegra: otg usb2/usb3 port init
  usb: hcd: Fix use after free in usb_hcd_pci_remove()
  usb: typec: ucsi: Hold con->lock for the entire duration of ucsi_register_port()
  ...
2 parent s 42df60f + 20934c0
Raw File
Tip revision: 6f0306d1bfbc4d3e3fcfb949ee2c4af21071920a authored by Linus Torvalds on 30 August 2020, 17:51:03 UTC
Merge tag 'usb-5.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Tip revision: 6f0306d
logic_pio.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
 * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
 * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
 * Author: John Garry <john.garry@huawei.com>
 */

#define pr_fmt(fmt)	"LOGIC PIO: " fmt

#include <linux/of.h>
#include <linux/io.h>
#include <linux/logic_pio.h>
#include <linux/mm.h>
#include <linux/rculist.h>
#include <linux/sizes.h>
#include <linux/slab.h>

/* The unique hardware address list */
static LIST_HEAD(io_range_list);
static DEFINE_MUTEX(io_range_mutex);

/* Consider a kernel general helper for this */
#define in_range(b, first, len)        ((b) >= (first) && (b) < (first) + (len))

/**
 * logic_pio_register_range - register logical PIO range for a host
 * @new_range: pointer to the IO range to be registered.
 *
 * Returns 0 on success, the error code in case of failure.
 *
 * Register a new IO range node in the IO range list.
 */
int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
{
	struct logic_pio_hwaddr *range;
	resource_size_t start;
	resource_size_t end;
	resource_size_t mmio_end = 0;
	resource_size_t iio_sz = MMIO_UPPER_LIMIT;
	int ret = 0;

	if (!new_range || !new_range->fwnode || !new_range->size ||
	    (new_range->flags == LOGIC_PIO_INDIRECT && !new_range->ops))
		return -EINVAL;

	start = new_range->hw_start;
	end = new_range->hw_start + new_range->size;

	mutex_lock(&io_range_mutex);
	list_for_each_entry(range, &io_range_list, list) {
		if (range->fwnode == new_range->fwnode) {
			/* range already there */
			goto end_register;
		}
		if (range->flags == LOGIC_PIO_CPU_MMIO &&
		    new_range->flags == LOGIC_PIO_CPU_MMIO) {
			/* for MMIO ranges we need to check for overlap */
			if (start >= range->hw_start + range->size ||
			    end < range->hw_start) {
				mmio_end = range->io_start + range->size;
			} else {
				ret = -EFAULT;
				goto end_register;
			}
		} else if (range->flags == LOGIC_PIO_INDIRECT &&
			   new_range->flags == LOGIC_PIO_INDIRECT) {
			iio_sz += range->size;
		}
	}

	/* range not registered yet, check for available space */
	if (new_range->flags == LOGIC_PIO_CPU_MMIO) {
		if (mmio_end + new_range->size - 1 > MMIO_UPPER_LIMIT) {
			/* if it's too big check if 64K space can be reserved */
			if (mmio_end + SZ_64K - 1 > MMIO_UPPER_LIMIT) {
				ret = -E2BIG;
				goto end_register;
			}
			new_range->size = SZ_64K;
			pr_warn("Requested IO range too big, new size set to 64K\n");
		}
		new_range->io_start = mmio_end;
	} else if (new_range->flags == LOGIC_PIO_INDIRECT) {
		if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) {
			ret = -E2BIG;
			goto end_register;
		}
		new_range->io_start = iio_sz;
	} else {
		/* invalid flag */
		ret = -EINVAL;
		goto end_register;
	}

	list_add_tail_rcu(&new_range->list, &io_range_list);

end_register:
	mutex_unlock(&io_range_mutex);
	return ret;
}

/**
 * logic_pio_unregister_range - unregister a logical PIO range for a host
 * @range: pointer to the IO range which has been already registered.
 *
 * Unregister a previously-registered IO range node.
 */
void logic_pio_unregister_range(struct logic_pio_hwaddr *range)
{
	mutex_lock(&io_range_mutex);
	list_del_rcu(&range->list);
	mutex_unlock(&io_range_mutex);
	synchronize_rcu();
}

/**
 * find_io_range_by_fwnode - find logical PIO range for given FW node
 * @fwnode: FW node handle associated with logical PIO range
 *
 * Returns pointer to node on success, NULL otherwise.
 *
 * Traverse the io_range_list to find the registered node for @fwnode.
 */
struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
{
	struct logic_pio_hwaddr *range, *found_range = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (range->fwnode == fwnode) {
			found_range = range;
			break;
		}
	}
	rcu_read_unlock();

	return found_range;
}

/* Return a registered range given an input PIO token */
static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
{
	struct logic_pio_hwaddr *range, *found_range = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (in_range(pio, range->io_start, range->size)) {
			found_range = range;
			break;
		}
	}
	rcu_read_unlock();

	if (!found_range)
		pr_err("PIO entry token 0x%lx invalid\n", pio);

	return found_range;
}

/**
 * logic_pio_to_hwaddr - translate logical PIO to HW address
 * @pio: logical PIO value
 *
 * Returns HW address if valid, ~0 otherwise.
 *
 * Translate the input logical PIO to the corresponding hardware address.
 * The input PIO should be unique in the whole logical PIO space.
 */
resource_size_t logic_pio_to_hwaddr(unsigned long pio)
{
	struct logic_pio_hwaddr *range;

	range = find_io_range(pio);
	if (range)
		return range->hw_start + pio - range->io_start;

	return (resource_size_t)~0;
}

/**
 * logic_pio_trans_hwaddr - translate HW address to logical PIO
 * @fwnode: FW node reference for the host
 * @addr: Host-relative HW address
 * @size: size to translate
 *
 * Returns Logical PIO value if successful, ~0UL otherwise
 */
unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
				     resource_size_t addr, resource_size_t size)
{
	struct logic_pio_hwaddr *range;

	range = find_io_range_by_fwnode(fwnode);
	if (!range || range->flags == LOGIC_PIO_CPU_MMIO) {
		pr_err("IO range not found or invalid\n");
		return ~0UL;
	}
	if (range->size < size) {
		pr_err("resource size %pa cannot fit in IO range size %pa\n",
		       &size, &range->size);
		return ~0UL;
	}
	return addr - range->hw_start + range->io_start;
}

unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
{
	struct logic_pio_hwaddr *range;

	rcu_read_lock();
	list_for_each_entry_rcu(range, &io_range_list, list) {
		if (range->flags != LOGIC_PIO_CPU_MMIO)
			continue;
		if (in_range(addr, range->hw_start, range->size)) {
			unsigned long cpuaddr;

			cpuaddr = addr - range->hw_start + range->io_start;

			rcu_read_unlock();
			return cpuaddr;
		}
	}
	rcu_read_unlock();

	pr_err("addr %pa not registered in io_range_list\n", &addr);

	return ~0UL;
}

#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
#define BUILD_LOGIC_IO(bwl, type)					\
type logic_in##bwl(unsigned long addr)					\
{									\
	type ret = (type)~0;						\
									\
	if (addr < MMIO_UPPER_LIMIT) {					\
		ret = _in##bwl(addr);					\
	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
									\
		if (entry)						\
			ret = entry->ops->in(entry->hostdata,		\
					addr, sizeof(type));		\
		else							\
			WARN_ON_ONCE(1);				\
	}								\
	return ret;							\
}									\
									\
void logic_out##bwl(type value, unsigned long addr)			\
{									\
	if (addr < MMIO_UPPER_LIMIT) {					\
		_out##bwl(value, addr);				\
	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
									\
		if (entry)						\
			entry->ops->out(entry->hostdata,		\
					addr, value, sizeof(type));	\
		else							\
			WARN_ON_ONCE(1);				\
	}								\
}									\
									\
void logic_ins##bwl(unsigned long addr, void *buffer,			\
		    unsigned int count)					\
{									\
	if (addr < MMIO_UPPER_LIMIT) {					\
		reads##bwl(PCI_IOBASE + addr, buffer, count);		\
	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
									\
		if (entry)						\
			entry->ops->ins(entry->hostdata,		\
				addr, buffer, sizeof(type), count);	\
		else							\
			WARN_ON_ONCE(1);				\
	}								\
									\
}									\
									\
void logic_outs##bwl(unsigned long addr, const void *buffer,		\
		     unsigned int count)				\
{									\
	if (addr < MMIO_UPPER_LIMIT) {					\
		writes##bwl(PCI_IOBASE + addr, buffer, count);		\
	} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) {	\
		struct logic_pio_hwaddr *entry = find_io_range(addr);	\
									\
		if (entry)						\
			entry->ops->outs(entry->hostdata,		\
				addr, buffer, sizeof(type), count);	\
		else							\
			WARN_ON_ONCE(1);				\
	}								\
}

BUILD_LOGIC_IO(b, u8)
EXPORT_SYMBOL(logic_inb);
EXPORT_SYMBOL(logic_insb);
EXPORT_SYMBOL(logic_outb);
EXPORT_SYMBOL(logic_outsb);

BUILD_LOGIC_IO(w, u16)
EXPORT_SYMBOL(logic_inw);
EXPORT_SYMBOL(logic_insw);
EXPORT_SYMBOL(logic_outw);
EXPORT_SYMBOL(logic_outsw);

BUILD_LOGIC_IO(l, u32)
EXPORT_SYMBOL(logic_inl);
EXPORT_SYMBOL(logic_insl);
EXPORT_SYMBOL(logic_outl);
EXPORT_SYMBOL(logic_outsl);

#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */
back to top