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