2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2020-07-02 15:46:02 +01:00
/*
2015-05-13 15:26:42 +03:00
* ulpi . c - USB ULPI PHY bus
*
* Copyright ( C ) 2015 Intel Corporation
*
* Author : Heikki Krogerus < heikki . krogerus @ linux . intel . com >
*/
# include <linux/ulpi/interface.h>
# include <linux/ulpi/driver.h>
# include <linux/ulpi/regs.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/acpi.h>
2022-01-27 14:00:04 -05:00
# include <linux/debugfs.h>
2016-12-28 14:56:49 -08:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/clk/clk-conf.h>
2015-05-13 15:26:42 +03:00
/* -------------------------------------------------------------------------- */
int ulpi_read ( struct ulpi * ulpi , u8 addr )
{
2016-08-16 19:04:50 +03:00
return ulpi - > ops - > read ( ulpi - > dev . parent , addr ) ;
2015-05-13 15:26:42 +03:00
}
EXPORT_SYMBOL_GPL ( ulpi_read ) ;
int ulpi_write ( struct ulpi * ulpi , u8 addr , u8 val )
{
2016-08-16 19:04:50 +03:00
return ulpi - > ops - > write ( ulpi - > dev . parent , addr , val ) ;
2015-05-13 15:26:42 +03:00
}
EXPORT_SYMBOL_GPL ( ulpi_write ) ;
/* -------------------------------------------------------------------------- */
static int ulpi_match ( struct device * dev , struct device_driver * driver )
{
struct ulpi_driver * drv = to_ulpi_driver ( driver ) ;
struct ulpi * ulpi = to_ulpi_dev ( dev ) ;
const struct ulpi_device_id * id ;
2022-01-17 15:00:39 +00:00
/*
* Some ULPI devices don ' t have a vendor id
* or provide an id_table so rely on OF match .
*/
if ( ulpi - > id . vendor = = 0 | | ! drv - > id_table )
2016-12-28 14:56:49 -08:00
return of_driver_match_device ( dev , driver ) ;
2015-05-13 15:26:42 +03:00
for ( id = drv - > id_table ; id - > vendor ; id + + )
if ( id - > vendor = = ulpi - > id . vendor & &
id - > product = = ulpi - > id . product )
return 1 ;
return 0 ;
}
static int ulpi_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
struct ulpi * ulpi = to_ulpi_dev ( dev ) ;
2016-12-28 14:56:49 -08:00
int ret ;
ret = of_device_uevent_modalias ( dev , env ) ;
if ( ret ! = - ENODEV )
return ret ;
2015-05-13 15:26:42 +03:00
if ( add_uevent_var ( env , " MODALIAS=ulpi:v%04xp%04x " ,
ulpi - > id . vendor , ulpi - > id . product ) )
return - ENOMEM ;
return 0 ;
}
static int ulpi_probe ( struct device * dev )
{
struct ulpi_driver * drv = to_ulpi_driver ( dev - > driver ) ;
2016-12-28 14:56:49 -08:00
int ret ;
ret = of_clk_set_defaults ( dev - > of_node , false ) ;
if ( ret < 0 )
return ret ;
2015-05-13 15:26:42 +03:00
return drv - > probe ( to_ulpi_dev ( dev ) ) ;
}
2021-07-13 21:35:22 +02:00
static void ulpi_remove ( struct device * dev )
2015-05-13 15:26:42 +03:00
{
struct ulpi_driver * drv = to_ulpi_driver ( dev - > driver ) ;
if ( drv - > remove )
drv - > remove ( to_ulpi_dev ( dev ) ) ;
}
static struct bus_type ulpi_bus = {
. name = " ulpi " ,
. match = ulpi_match ,
. uevent = ulpi_uevent ,
. probe = ulpi_probe ,
. remove = ulpi_remove ,
} ;
/* -------------------------------------------------------------------------- */
static ssize_t modalias_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2016-12-28 14:56:49 -08:00
int len ;
2015-05-13 15:26:42 +03:00
struct ulpi * ulpi = to_ulpi_dev ( dev ) ;
2017-03-22 09:16:27 -05:00
len = of_device_modalias ( dev , buf , PAGE_SIZE ) ;
2016-12-28 14:56:49 -08:00
if ( len ! = - ENODEV )
return len ;
2015-05-13 15:26:42 +03:00
return sprintf ( buf , " ulpi:v%04xp%04x \n " ,
ulpi - > id . vendor , ulpi - > id . product ) ;
}
static DEVICE_ATTR_RO ( modalias ) ;
static struct attribute * ulpi_dev_attrs [ ] = {
& dev_attr_modalias . attr ,
NULL
} ;
2020-11-25 17:25:00 +01:00
static const struct attribute_group ulpi_dev_attr_group = {
2015-05-13 15:26:42 +03:00
. attrs = ulpi_dev_attrs ,
} ;
static const struct attribute_group * ulpi_dev_attr_groups [ ] = {
& ulpi_dev_attr_group ,
NULL
} ;
static void ulpi_dev_release ( struct device * dev )
{
2022-01-27 14:00:02 -05:00
of_node_put ( dev - > of_node ) ;
2015-05-13 15:26:42 +03:00
kfree ( to_ulpi_dev ( dev ) ) ;
}
2017-08-19 13:52:26 +05:30
static const struct device_type ulpi_dev_type = {
2015-05-13 15:26:42 +03:00
. name = " ulpi_device " ,
. groups = ulpi_dev_attr_groups ,
. release = ulpi_dev_release ,
} ;
/* -------------------------------------------------------------------------- */
/**
2021-05-26 14:00:16 +01:00
* __ulpi_register_driver - register a driver with the ULPI bus
2015-05-13 15:26:42 +03:00
* @ drv : driver being registered
2020-07-02 15:46:02 +01:00
* @ module : ends up being THIS_MODULE
2015-05-13 15:26:42 +03:00
*
* Registers a driver with the ULPI bus .
*/
2016-06-25 22:38:21 -07:00
int __ulpi_register_driver ( struct ulpi_driver * drv , struct module * module )
2015-05-13 15:26:42 +03:00
{
if ( ! drv - > probe )
return - EINVAL ;
2016-06-25 22:38:21 -07:00
drv - > driver . owner = module ;
2015-05-13 15:26:42 +03:00
drv - > driver . bus = & ulpi_bus ;
return driver_register ( & drv - > driver ) ;
}
2016-06-25 22:38:21 -07:00
EXPORT_SYMBOL_GPL ( __ulpi_register_driver ) ;
2015-05-13 15:26:42 +03:00
/**
* ulpi_unregister_driver - unregister a driver with the ULPI bus
* @ drv : driver to unregister
*
* Unregisters a driver with the ULPI bus .
*/
void ulpi_unregister_driver ( struct ulpi_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( ulpi_unregister_driver ) ;
/* -------------------------------------------------------------------------- */
2016-12-28 14:56:49 -08:00
static int ulpi_of_register ( struct ulpi * ulpi )
2015-05-13 15:26:42 +03:00
{
2016-12-28 14:56:49 -08:00
struct device_node * np = NULL , * child ;
struct device * parent ;
/* Find a ulpi bus underneath the parent or the grandparent */
parent = ulpi - > dev . parent ;
if ( parent - > of_node )
2017-11-11 16:31:18 +01:00
np = of_get_child_by_name ( parent - > of_node , " ulpi " ) ;
2016-12-28 14:56:49 -08:00
else if ( parent - > parent & & parent - > parent - > of_node )
2017-11-11 16:31:18 +01:00
np = of_get_child_by_name ( parent - > parent - > of_node , " ulpi " ) ;
2016-12-28 14:56:49 -08:00
if ( ! np )
return 0 ;
child = of_get_next_available_child ( np , NULL ) ;
of_node_put ( np ) ;
if ( ! child )
return - EINVAL ;
2015-05-13 15:26:42 +03:00
2016-12-28 14:56:49 -08:00
ulpi - > dev . of_node = child ;
return 0 ;
}
static int ulpi_read_id ( struct ulpi * ulpi )
{
int ret ;
2016-08-16 19:04:46 +03:00
2015-05-13 15:26:42 +03:00
/* Test the interface */
ret = ulpi_write ( ulpi , ULPI_SCRATCH , 0xaa ) ;
if ( ret < 0 )
2016-12-28 14:56:49 -08:00
goto err ;
2015-05-13 15:26:42 +03:00
ret = ulpi_read ( ulpi , ULPI_SCRATCH ) ;
if ( ret < 0 )
return ret ;
if ( ret ! = 0xaa )
2016-12-28 14:56:49 -08:00
goto err ;
2015-05-13 15:26:42 +03:00
ulpi - > id . vendor = ulpi_read ( ulpi , ULPI_VENDOR_ID_LOW ) ;
ulpi - > id . vendor | = ulpi_read ( ulpi , ULPI_VENDOR_ID_HIGH ) < < 8 ;
ulpi - > id . product = ulpi_read ( ulpi , ULPI_PRODUCT_ID_LOW ) ;
ulpi - > id . product | = ulpi_read ( ulpi , ULPI_PRODUCT_ID_HIGH ) < < 8 ;
2016-12-28 14:56:49 -08:00
/* Some ULPI devices don't have a vendor id so rely on OF match */
if ( ulpi - > id . vendor = = 0 )
goto err ;
request_module ( " ulpi:v%04xp%04x " , ulpi - > id . vendor , ulpi - > id . product ) ;
return 0 ;
err :
of_device_request_module ( & ulpi - > dev ) ;
return 0 ;
}
2022-09-22 22:25:05 +08:00
static int ulpi_regs_show ( struct seq_file * seq , void * data )
2022-01-27 14:00:04 -05:00
{
struct ulpi * ulpi = seq - > private ;
# define ulpi_print(name, reg) do { \
int ret = ulpi_read ( ulpi , reg ) ; \
if ( ret < 0 ) \
return ret ; \
seq_printf ( seq , name " %.02x \n " , ret ) ; \
} while ( 0 )
ulpi_print ( " Vendor ID Low " , ULPI_VENDOR_ID_LOW ) ;
ulpi_print ( " Vendor ID High " , ULPI_VENDOR_ID_HIGH ) ;
ulpi_print ( " Product ID Low " , ULPI_PRODUCT_ID_LOW ) ;
ulpi_print ( " Product ID High " , ULPI_PRODUCT_ID_HIGH ) ;
ulpi_print ( " Function Control " , ULPI_FUNC_CTRL ) ;
ulpi_print ( " Interface Control " , ULPI_IFC_CTRL ) ;
ulpi_print ( " OTG Control " , ULPI_OTG_CTRL ) ;
ulpi_print ( " USB Interrupt Enable Rising " , ULPI_USB_INT_EN_RISE ) ;
ulpi_print ( " USB Interrupt Enable Falling " , ULPI_USB_INT_EN_FALL ) ;
ulpi_print ( " USB Interrupt Status " , ULPI_USB_INT_STS ) ;
ulpi_print ( " USB Interrupt Latch " , ULPI_USB_INT_LATCH ) ;
ulpi_print ( " Debug " , ULPI_DEBUG ) ;
ulpi_print ( " Scratch Register " , ULPI_SCRATCH ) ;
ulpi_print ( " Carkit Control " , ULPI_CARKIT_CTRL ) ;
ulpi_print ( " Carkit Interrupt Delay " , ULPI_CARKIT_INT_DELAY ) ;
ulpi_print ( " Carkit Interrupt Enable " , ULPI_CARKIT_INT_EN ) ;
ulpi_print ( " Carkit Interrupt Status " , ULPI_CARKIT_INT_STS ) ;
ulpi_print ( " Carkit Interrupt Latch " , ULPI_CARKIT_INT_LATCH ) ;
ulpi_print ( " Carkit Pulse Control " , ULPI_CARKIT_PLS_CTRL ) ;
ulpi_print ( " Transmit Positive Width " , ULPI_TX_POS_WIDTH ) ;
ulpi_print ( " Transmit Negative Width " , ULPI_TX_NEG_WIDTH ) ;
ulpi_print ( " Receive Polarity Recovery " , ULPI_POLARITY_RECOVERY ) ;
return 0 ;
}
2022-09-22 22:25:05 +08:00
DEFINE_SHOW_ATTRIBUTE ( ulpi_regs ) ;
2022-01-27 14:00:04 -05:00
# define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL)
2016-12-28 14:56:49 -08:00
static int ulpi_register ( struct device * dev , struct ulpi * ulpi )
{
int ret ;
2022-01-27 14:00:04 -05:00
struct dentry * root ;
2016-12-28 14:56:49 -08:00
ulpi - > dev . parent = dev ; /* needed early for ops */
2015-05-13 15:26:42 +03:00
ulpi - > dev . bus = & ulpi_bus ;
ulpi - > dev . type = & ulpi_dev_type ;
dev_set_name ( & ulpi - > dev , " %s.ulpi " , dev_name ( dev ) ) ;
ACPI_COMPANION_SET ( & ulpi - > dev , ACPI_COMPANION ( dev ) ) ;
2016-12-28 14:56:49 -08:00
ret = ulpi_of_register ( ulpi ) ;
if ( ret )
return ret ;
ret = ulpi_read_id ( ulpi ) ;
2022-01-27 14:00:03 -05:00
if ( ret ) {
of_node_put ( ulpi - > dev . of_node ) ;
2016-12-28 14:56:49 -08:00
return ret ;
2022-01-27 14:00:03 -05:00
}
2015-05-13 15:26:42 +03:00
ret = device_register ( & ulpi - > dev ) ;
2022-01-27 14:00:03 -05:00
if ( ret ) {
put_device ( & ulpi - > dev ) ;
2015-05-13 15:26:42 +03:00
return ret ;
2022-01-27 14:00:03 -05:00
}
2015-05-13 15:26:42 +03:00
2022-01-27 14:00:04 -05:00
root = debugfs_create_dir ( dev_name ( dev ) , ULPI_ROOT ) ;
2022-09-22 22:25:05 +08:00
debugfs_create_file ( " regs " , 0444 , root , ulpi , & ulpi_regs_fops ) ;
2022-01-27 14:00:04 -05:00
2015-05-13 15:26:42 +03:00
dev_dbg ( & ulpi - > dev , " registered ULPI PHY: vendor %04x, product %04x \n " ,
ulpi - > id . vendor , ulpi - > id . product ) ;
return 0 ;
}
/**
* ulpi_register_interface - instantiate new ULPI device
* @ dev : USB controller ' s device interface
* @ ops : ULPI register access
*
* Allocates and registers a ULPI device and an interface for it . Called from
* the USB controller that provides the ULPI interface .
*/
2016-08-16 19:04:52 +03:00
struct ulpi * ulpi_register_interface ( struct device * dev ,
const struct ulpi_ops * ops )
2015-05-13 15:26:42 +03:00
{
struct ulpi * ulpi ;
int ret ;
ulpi = kzalloc ( sizeof ( * ulpi ) , GFP_KERNEL ) ;
if ( ! ulpi )
return ERR_PTR ( - ENOMEM ) ;
ulpi - > ops = ops ;
ret = ulpi_register ( dev , ulpi ) ;
if ( ret ) {
kfree ( ulpi ) ;
return ERR_PTR ( ret ) ;
}
return ulpi ;
}
EXPORT_SYMBOL_GPL ( ulpi_register_interface ) ;
/**
* ulpi_unregister_interface - unregister ULPI interface
2020-07-02 15:46:02 +01:00
* @ ulpi : struct ulpi_interface
2015-05-13 15:26:42 +03:00
*
* Unregisters a ULPI device and it ' s interface that was created with
* ulpi_create_interface ( ) .
*/
void ulpi_unregister_interface ( struct ulpi * ulpi )
{
2022-01-27 14:00:04 -05:00
debugfs_remove_recursive ( debugfs_lookup ( dev_name ( & ulpi - > dev ) ,
ULPI_ROOT ) ) ;
2015-05-13 15:26:42 +03:00
device_unregister ( & ulpi - > dev ) ;
}
EXPORT_SYMBOL_GPL ( ulpi_unregister_interface ) ;
/* -------------------------------------------------------------------------- */
static int __init ulpi_init ( void )
{
2022-01-27 14:00:04 -05:00
int ret ;
struct dentry * root ;
root = debugfs_create_dir ( KBUILD_MODNAME , NULL ) ;
ret = bus_register ( & ulpi_bus ) ;
if ( ret )
debugfs_remove ( root ) ;
return ret ;
2015-05-13 15:26:42 +03:00
}
2015-06-04 20:44:14 +08:00
subsys_initcall ( ulpi_init ) ;
2015-05-13 15:26:42 +03:00
static void __exit ulpi_exit ( void )
{
bus_unregister ( & ulpi_bus ) ;
2022-01-27 14:00:04 -05:00
debugfs_remove_recursive ( ULPI_ROOT ) ;
2015-05-13 15:26:42 +03:00
}
module_exit ( ulpi_exit ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " USB ULPI PHY bus " ) ;