18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ulpi.c - USB ULPI PHY bus
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Intel Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/ulpi/interface.h>
118c2ecf20Sopenharmony_ci#include <linux/ulpi/driver.h>
128c2ecf20Sopenharmony_ci#include <linux/ulpi/regs.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/acpi.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_device.h>
188c2ecf20Sopenharmony_ci#include <linux/clk/clk-conf.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciint ulpi_read(struct ulpi *ulpi, u8 addr)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	return ulpi->ops->read(ulpi->dev.parent, addr);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ulpi_read);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciint ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return ulpi->ops->write(ulpi->dev.parent, addr, val);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ulpi_write);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int ulpi_match(struct device *dev, struct device_driver *driver)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct ulpi_driver *drv = to_ulpi_driver(driver);
398c2ecf20Sopenharmony_ci	struct ulpi *ulpi = to_ulpi_dev(dev);
408c2ecf20Sopenharmony_ci	const struct ulpi_device_id *id;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/*
438c2ecf20Sopenharmony_ci	 * Some ULPI devices don't have a vendor id
448c2ecf20Sopenharmony_ci	 * or provide an id_table so rely on OF match.
458c2ecf20Sopenharmony_ci	 */
468c2ecf20Sopenharmony_ci	if (ulpi->id.vendor == 0 || !drv->id_table)
478c2ecf20Sopenharmony_ci		return of_driver_match_device(dev, driver);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	for (id = drv->id_table; id->vendor; id++)
508c2ecf20Sopenharmony_ci		if (id->vendor == ulpi->id.vendor &&
518c2ecf20Sopenharmony_ci		    id->product == ulpi->id.product)
528c2ecf20Sopenharmony_ci			return 1;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct ulpi *ulpi = to_ulpi_dev(dev);
608c2ecf20Sopenharmony_ci	int ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	ret = of_device_uevent_modalias(dev, env);
638c2ecf20Sopenharmony_ci	if (ret != -ENODEV)
648c2ecf20Sopenharmony_ci		return ret;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
678c2ecf20Sopenharmony_ci			   ulpi->id.vendor, ulpi->id.product))
688c2ecf20Sopenharmony_ci		return -ENOMEM;
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int ulpi_probe(struct device *dev)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
758c2ecf20Sopenharmony_ci	int ret;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	ret = of_clk_set_defaults(dev->of_node, false);
788c2ecf20Sopenharmony_ci	if (ret < 0)
798c2ecf20Sopenharmony_ci		return ret;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return drv->probe(to_ulpi_dev(dev));
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int ulpi_remove(struct device *dev)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (drv->remove)
898c2ecf20Sopenharmony_ci		drv->remove(to_ulpi_dev(dev));
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct bus_type ulpi_bus = {
958c2ecf20Sopenharmony_ci	.name = "ulpi",
968c2ecf20Sopenharmony_ci	.match = ulpi_match,
978c2ecf20Sopenharmony_ci	.uevent = ulpi_uevent,
988c2ecf20Sopenharmony_ci	.probe = ulpi_probe,
998c2ecf20Sopenharmony_ci	.remove = ulpi_remove,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
1058c2ecf20Sopenharmony_ci			     char *buf)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int len;
1088c2ecf20Sopenharmony_ci	struct ulpi *ulpi = to_ulpi_dev(dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	len = of_device_modalias(dev, buf, PAGE_SIZE);
1118c2ecf20Sopenharmony_ci	if (len != -ENODEV)
1128c2ecf20Sopenharmony_ci		return len;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return sprintf(buf, "ulpi:v%04xp%04x\n",
1158c2ecf20Sopenharmony_ci		       ulpi->id.vendor, ulpi->id.product);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct attribute *ulpi_dev_attrs[] = {
1208c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
1218c2ecf20Sopenharmony_ci	NULL
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic struct attribute_group ulpi_dev_attr_group = {
1258c2ecf20Sopenharmony_ci	.attrs = ulpi_dev_attrs,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const struct attribute_group *ulpi_dev_attr_groups[] = {
1298c2ecf20Sopenharmony_ci	&ulpi_dev_attr_group,
1308c2ecf20Sopenharmony_ci	NULL
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void ulpi_dev_release(struct device *dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	of_node_put(dev->of_node);
1368c2ecf20Sopenharmony_ci	kfree(to_ulpi_dev(dev));
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const struct device_type ulpi_dev_type = {
1408c2ecf20Sopenharmony_ci	.name = "ulpi_device",
1418c2ecf20Sopenharmony_ci	.groups = ulpi_dev_attr_groups,
1428c2ecf20Sopenharmony_ci	.release = ulpi_dev_release,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/**
1488c2ecf20Sopenharmony_ci * ulpi_register_driver - register a driver with the ULPI bus
1498c2ecf20Sopenharmony_ci * @drv: driver being registered
1508c2ecf20Sopenharmony_ci * @module: ends up being THIS_MODULE
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci * Registers a driver with the ULPI bus.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ciint __ulpi_register_driver(struct ulpi_driver *drv, struct module *module)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	if (!drv->probe)
1578c2ecf20Sopenharmony_ci		return -EINVAL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	drv->driver.owner = module;
1608c2ecf20Sopenharmony_ci	drv->driver.bus = &ulpi_bus;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__ulpi_register_driver);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/**
1678c2ecf20Sopenharmony_ci * ulpi_unregister_driver - unregister a driver with the ULPI bus
1688c2ecf20Sopenharmony_ci * @drv: driver to unregister
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Unregisters a driver with the ULPI bus.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_civoid ulpi_unregister_driver(struct ulpi_driver *drv)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ulpi_unregister_driver);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int ulpi_of_register(struct ulpi *ulpi)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct device_node *np = NULL, *child;
1838c2ecf20Sopenharmony_ci	struct device *parent;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Find a ulpi bus underneath the parent or the grandparent */
1868c2ecf20Sopenharmony_ci	parent = ulpi->dev.parent;
1878c2ecf20Sopenharmony_ci	if (parent->of_node)
1888c2ecf20Sopenharmony_ci		np = of_get_child_by_name(parent->of_node, "ulpi");
1898c2ecf20Sopenharmony_ci	else if (parent->parent && parent->parent->of_node)
1908c2ecf20Sopenharmony_ci		np = of_get_child_by_name(parent->parent->of_node, "ulpi");
1918c2ecf20Sopenharmony_ci	if (!np)
1928c2ecf20Sopenharmony_ci		return 0;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	child = of_get_next_available_child(np, NULL);
1958c2ecf20Sopenharmony_ci	of_node_put(np);
1968c2ecf20Sopenharmony_ci	if (!child)
1978c2ecf20Sopenharmony_ci		return -EINVAL;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ulpi->dev.of_node = child;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int ulpi_read_id(struct ulpi *ulpi)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Test the interface */
2098c2ecf20Sopenharmony_ci	ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
2108c2ecf20Sopenharmony_ci	if (ret < 0)
2118c2ecf20Sopenharmony_ci		goto err;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ret = ulpi_read(ulpi, ULPI_SCRATCH);
2148c2ecf20Sopenharmony_ci	if (ret < 0)
2158c2ecf20Sopenharmony_ci		return ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (ret != 0xaa)
2188c2ecf20Sopenharmony_ci		goto err;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
2218c2ecf20Sopenharmony_ci	ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
2248c2ecf20Sopenharmony_ci	ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Some ULPI devices don't have a vendor id so rely on OF match */
2278c2ecf20Sopenharmony_ci	if (ulpi->id.vendor == 0)
2288c2ecf20Sopenharmony_ci		goto err;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
2318c2ecf20Sopenharmony_ci	return 0;
2328c2ecf20Sopenharmony_cierr:
2338c2ecf20Sopenharmony_ci	of_device_request_module(&ulpi->dev);
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int ulpi_register(struct device *dev, struct ulpi *ulpi)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ulpi->dev.parent = dev; /* needed early for ops */
2428c2ecf20Sopenharmony_ci	ulpi->dev.bus = &ulpi_bus;
2438c2ecf20Sopenharmony_ci	ulpi->dev.type = &ulpi_dev_type;
2448c2ecf20Sopenharmony_ci	dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	ret = ulpi_of_register(ulpi);
2498c2ecf20Sopenharmony_ci	if (ret)
2508c2ecf20Sopenharmony_ci		return ret;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ret = ulpi_read_id(ulpi);
2538c2ecf20Sopenharmony_ci	if (ret) {
2548c2ecf20Sopenharmony_ci		of_node_put(ulpi->dev.of_node);
2558c2ecf20Sopenharmony_ci		return ret;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	ret = device_register(&ulpi->dev);
2598c2ecf20Sopenharmony_ci	if (ret) {
2608c2ecf20Sopenharmony_ci		put_device(&ulpi->dev);
2618c2ecf20Sopenharmony_ci		return ret;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
2658c2ecf20Sopenharmony_ci		ulpi->id.vendor, ulpi->id.product);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/**
2718c2ecf20Sopenharmony_ci * ulpi_register_interface - instantiate new ULPI device
2728c2ecf20Sopenharmony_ci * @dev: USB controller's device interface
2738c2ecf20Sopenharmony_ci * @ops: ULPI register access
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci * Allocates and registers a ULPI device and an interface for it. Called from
2768c2ecf20Sopenharmony_ci * the USB controller that provides the ULPI interface.
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_cistruct ulpi *ulpi_register_interface(struct device *dev,
2798c2ecf20Sopenharmony_ci				     const struct ulpi_ops *ops)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct ulpi *ulpi;
2828c2ecf20Sopenharmony_ci	int ret;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
2858c2ecf20Sopenharmony_ci	if (!ulpi)
2868c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ulpi->ops = ops;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	ret = ulpi_register(dev, ulpi);
2918c2ecf20Sopenharmony_ci	if (ret) {
2928c2ecf20Sopenharmony_ci		kfree(ulpi);
2938c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return ulpi;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ulpi_register_interface);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/**
3018c2ecf20Sopenharmony_ci * ulpi_unregister_interface - unregister ULPI interface
3028c2ecf20Sopenharmony_ci * @ulpi: struct ulpi_interface
3038c2ecf20Sopenharmony_ci *
3048c2ecf20Sopenharmony_ci * Unregisters a ULPI device and it's interface that was created with
3058c2ecf20Sopenharmony_ci * ulpi_create_interface().
3068c2ecf20Sopenharmony_ci */
3078c2ecf20Sopenharmony_civoid ulpi_unregister_interface(struct ulpi *ulpi)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	device_unregister(&ulpi->dev);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ulpi_unregister_interface);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int __init ulpi_init(void)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	return bus_register(&ulpi_bus);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_cisubsys_initcall(ulpi_init);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void __exit ulpi_exit(void)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	bus_unregister(&ulpi_bus);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_cimodule_exit(ulpi_exit);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
3288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB ULPI PHY bus");
330