2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2006-06-29 18:28:18 +04:00
/*
2012-03-13 01:54:50 +04:00
* driver for NXP USB Host devices
2006-06-29 18:28:18 +04:00
*
2012-03-13 01:54:50 +04:00
* Currently supported OHCI host devices :
2012-03-13 01:54:51 +04:00
* - NXP LPC32xx
2006-06-29 18:28:18 +04:00
*
* Authors : Dmitry Chigirev < source @ mvista . com >
2006-12-05 14:18:31 +03:00
* Vitaly Wool < vitalywool @ gmail . com >
2006-06-29 18:28:18 +04:00
*
* register initialization is based on code examples provided by Philips
* Copyright ( c ) 2005 Koninklijke Philips Electronics N . V .
*
* NOTE : This driver does not have suspend / resume functionality
* This driver is intended for engineering development purposes only
*
2017-11-06 17:37:25 +03:00
* 2005 - 2006 ( c ) MontaVista Software , Inc .
2006-06-29 18:28:18 +04:00
*/
# include <linux/clk.h>
2013-09-21 15:08:44 +04:00
# include <linux/dma-mapping.h>
# include <linux/io.h>
2006-06-29 18:28:18 +04:00
# include <linux/i2c.h>
2013-09-21 15:08:44 +04:00
# include <linux/module.h>
2012-04-29 18:47:03 +04:00
# include <linux/of.h>
2013-09-21 15:08:44 +04:00
# include <linux/platform_device.h>
2012-04-29 18:47:06 +04:00
# include <linux/usb/isp1301.h>
2013-09-21 15:08:44 +04:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
# include "ohci.h"
2012-03-13 01:54:51 +04:00
# define USB_CONFIG_BASE 0x31020000
2006-06-29 18:28:18 +04:00
/* USB_OTG_STAT_CONTROL bit defines */
# define TRANSPARENT_I2C_EN (1 << 7)
# define HOST_EN (1 << 0)
2012-03-13 01:54:51 +04:00
/* On LPC32xx, those are undefined */
# ifndef start_int_set_falling_edge
# define start_int_set_falling_edge(irq)
# define start_int_set_rising_edge(irq)
# define start_int_ack(irq)
# define start_int_mask(irq)
# define start_int_umask(irq)
# endif
2013-09-21 15:08:44 +04:00
# define DRIVER_DESC "OHCI NXP driver"
static const char hcd_name [ ] = " ohci-nxp " ;
static struct hc_driver __read_mostly ohci_nxp_hc_driver ;
2009-10-01 21:03:13 +04:00
static struct i2c_client * isp1301_i2c_client ;
2006-06-29 18:28:18 +04:00
2016-03-03 07:19:36 +03:00
static struct clk * usb_host_clk ;
2006-06-29 18:28:18 +04:00
2012-03-13 01:54:51 +04:00
static void isp1301_configure_lpc32xx ( void )
{
/* LPC32XX only supports DAT_SE0 USB mode */
/* This sequence is important */
/* Disable transparent UART mode first */
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
( ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR ) ,
MC1_UART_EN ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
( ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR ) ,
~ MC1_SPEED_REG ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_MODE_CONTROL_1 , MC1_SPEED_REG ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
( ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR ) ,
~ 0 ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_MODE_CONTROL_2 ,
( MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL ) ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
( ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR ) , ~ 0 ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_MODE_CONTROL_1 , MC1_DAT_SE0 ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_OTG_CONTROL_1 ,
( OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN ) ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
( ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR ) ,
( OTG1_DM_PULLUP | OTG1_DP_PULLUP ) ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR , ~ 0 ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR ,
~ 0 ) ;
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR , ~ 0 ) ;
printk ( KERN_INFO " ISP1301 Vendor ID : 0x%04x \n " ,
i2c_smbus_read_word_data ( isp1301_i2c_client , 0x00 ) ) ;
printk ( KERN_INFO " ISP1301 Product ID : 0x%04x \n " ,
i2c_smbus_read_word_data ( isp1301_i2c_client , 0x02 ) ) ;
printk ( KERN_INFO " ISP1301 Version ID : 0x%04x \n " ,
i2c_smbus_read_word_data ( isp1301_i2c_client , 0x14 ) ) ;
}
static void isp1301_configure ( void )
{
2012-08-26 18:30:37 +04:00
isp1301_configure_lpc32xx ( ) ;
2006-06-29 18:28:18 +04:00
}
static inline void isp1301_vbus_on ( void )
{
2012-03-13 01:54:52 +04:00
i2c_smbus_write_byte_data ( isp1301_i2c_client , ISP1301_I2C_OTG_CONTROL_1 ,
OTG1_VBUS_DRV ) ;
2006-06-29 18:28:18 +04:00
}
static inline void isp1301_vbus_off ( void )
{
2012-03-13 01:54:52 +04:00
i2c_smbus_write_byte_data ( isp1301_i2c_client ,
ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR ,
OTG1_VBUS_DRV ) ;
2006-06-29 18:28:18 +04:00
}
2013-09-21 15:08:44 +04:00
static void ohci_nxp_start_hc ( void )
2006-06-29 18:28:18 +04:00
{
2019-08-09 17:40:27 +03:00
void __iomem * usb_otg_stat_control = ioremap ( USB_CONFIG_BASE + 0x110 , 4 ) ;
unsigned long tmp ;
if ( WARN_ON ( ! usb_otg_stat_control ) )
return ;
tmp = __raw_readl ( usb_otg_stat_control ) | HOST_EN ;
2016-12-08 02:54:11 +03:00
2019-08-09 17:40:27 +03:00
__raw_writel ( tmp , usb_otg_stat_control ) ;
2006-06-29 18:28:18 +04:00
isp1301_vbus_on ( ) ;
2019-08-09 17:40:27 +03:00
iounmap ( usb_otg_stat_control ) ;
2006-06-29 18:28:18 +04:00
}
2013-09-21 15:08:44 +04:00
static void ohci_nxp_stop_hc ( void )
2006-06-29 18:28:18 +04:00
{
2019-08-09 17:40:27 +03:00
void __iomem * usb_otg_stat_control = ioremap ( USB_CONFIG_BASE + 0x110 , 4 ) ;
2006-06-29 18:28:18 +04:00
unsigned long tmp ;
2016-12-08 02:54:11 +03:00
2019-08-09 17:40:27 +03:00
if ( WARN_ON ( ! usb_otg_stat_control ) )
return ;
2006-06-29 18:28:18 +04:00
isp1301_vbus_off ( ) ;
2019-08-09 17:40:27 +03:00
tmp = __raw_readl ( usb_otg_stat_control ) & ~ HOST_EN ;
__raw_writel ( tmp , usb_otg_stat_control ) ;
iounmap ( usb_otg_stat_control ) ;
2006-06-29 18:28:18 +04:00
}
2013-09-21 15:08:44 +04:00
static int ohci_hcd_nxp_probe ( struct platform_device * pdev )
2006-06-29 18:28:18 +04:00
{
2019-10-15 17:19:45 +03:00
struct usb_hcd * hcd = NULL ;
2012-03-13 01:54:50 +04:00
const struct hc_driver * driver = & ohci_nxp_hc_driver ;
2012-04-29 18:47:02 +04:00
struct resource * res ;
2006-06-29 18:28:18 +04:00
int ret = 0 , irq ;
2012-04-29 18:47:06 +04:00
struct device_node * isp1301_node ;
if ( pdev - > dev . of_node ) {
isp1301_node = of_parse_phandle ( pdev - > dev . of_node ,
" transceiver " , 0 ) ;
} else {
isp1301_node = NULL ;
}
isp1301_i2c_client = isp1301_get_client ( isp1301_node ) ;
2022-06-03 17:12:30 +03:00
of_node_put ( isp1301_node ) ;
2016-12-08 02:54:11 +03:00
if ( ! isp1301_i2c_client )
2013-05-10 10:22:42 +04:00
return - EPROBE_DEFER ;
2006-06-29 18:28:18 +04:00
2013-06-27 15:36:37 +04:00
ret = dma_coerce_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
2013-06-10 19:28:49 +04:00
if ( ret )
goto fail_disable ;
2012-04-29 18:47:02 +04:00
2012-03-13 01:54:50 +04:00
dev_dbg ( & pdev - > dev , " %s: " DRIVER_DESC " (nxp) \n " , hcd_name ) ;
2006-06-29 18:28:18 +04:00
if ( usb_disabled ( ) ) {
2012-04-27 22:24:41 +04:00
dev_err ( & pdev - > dev , " USB is disabled \n " ) ;
2006-06-29 18:28:18 +04:00
ret = - ENODEV ;
2013-05-10 10:22:42 +04:00
goto fail_disable ;
2006-06-29 18:28:18 +04:00
}
2016-03-03 07:19:36 +03:00
/* Enable USB host clock */
usb_host_clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( usb_host_clk ) ) {
dev_err ( & pdev - > dev , " failed to acquire USB OHCI clock \n " ) ;
ret = PTR_ERR ( usb_host_clk ) ;
2013-12-11 11:30:35 +04:00
goto fail_disable ;
2006-06-29 18:28:18 +04:00
}
2016-03-03 07:19:36 +03:00
ret = clk_prepare_enable ( usb_host_clk ) ;
2006-06-29 18:28:18 +04:00
if ( ret < 0 ) {
2016-03-03 07:19:36 +03:00
dev_err ( & pdev - > dev , " failed to start USB OHCI clock \n " ) ;
2013-12-11 11:30:35 +04:00
goto fail_disable ;
2006-06-29 18:28:18 +04:00
}
2012-06-20 16:02:23 +04:00
isp1301_configure ( ) ;
2006-06-29 18:28:18 +04:00
2012-04-29 18:47:02 +04:00
hcd = usb_create_hcd ( driver , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
2006-06-29 18:28:18 +04:00
if ( ! hcd ) {
2012-04-27 22:24:41 +04:00
dev_err ( & pdev - > dev , " Failed to allocate HC buffer \n " ) ;
2006-06-29 18:28:18 +04:00
ret = - ENOMEM ;
2013-05-10 10:22:42 +04:00
goto fail_hcd ;
2006-06-29 18:28:18 +04:00
}
2023-07-26 14:38:05 +03:00
hcd - > regs = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res ) ;
2013-01-21 14:09:22 +04:00
if ( IS_ERR ( hcd - > regs ) ) {
ret = PTR_ERR ( hcd - > regs ) ;
2013-05-10 10:22:42 +04:00
goto fail_resource ;
2006-06-29 18:28:18 +04:00
}
2012-04-29 18:47:02 +04:00
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2006-06-29 18:28:18 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
ret = - ENXIO ;
2013-05-10 10:22:42 +04:00
goto fail_resource ;
2006-06-29 18:28:18 +04:00
}
2013-09-21 15:08:44 +04:00
ohci_nxp_start_hc ( ) ;
2006-06-29 18:28:18 +04:00
platform_set_drvdata ( pdev , hcd ) ;
dev_info ( & pdev - > dev , " at 0x%p, irq %d \n " , hcd - > regs , hcd - > irq ) ;
2011-09-07 12:10:52 +04:00
ret = usb_add_hcd ( hcd , irq , 0 ) ;
2013-11-05 06:46:02 +04:00
if ( ret = = 0 ) {
device_wakeup_enable ( hcd - > self . controller ) ;
2006-06-29 18:28:18 +04:00
return ret ;
2013-11-05 06:46:02 +04:00
}
2006-06-29 18:28:18 +04:00
2013-09-21 15:08:44 +04:00
ohci_nxp_stop_hc ( ) ;
2013-05-10 10:22:42 +04:00
fail_resource :
2006-06-29 18:28:18 +04:00
usb_put_hcd ( hcd ) ;
2013-05-10 10:22:42 +04:00
fail_hcd :
2016-03-03 07:19:36 +03:00
clk_disable_unprepare ( usb_host_clk ) ;
2013-05-10 10:22:42 +04:00
fail_disable :
2008-11-13 20:57:53 +03:00
isp1301_i2c_client = NULL ;
2006-06-29 18:28:18 +04:00
return ret ;
}
2023-05-18 02:02:09 +03:00
static void ohci_hcd_nxp_remove ( struct platform_device * pdev )
2006-06-29 18:28:18 +04:00
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
usb_remove_hcd ( hcd ) ;
2013-09-21 15:08:44 +04:00
ohci_nxp_stop_hc ( ) ;
2006-06-29 18:28:18 +04:00
usb_put_hcd ( hcd ) ;
2016-03-03 07:19:36 +03:00
clk_disable_unprepare ( usb_host_clk ) ;
2008-11-13 20:57:53 +03:00
isp1301_i2c_client = NULL ;
2006-06-29 18:28:18 +04:00
}
2008-04-11 08:29:22 +04:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:usb-ohci " ) ;
2012-04-29 18:47:03 +04:00
# ifdef CONFIG_OF
2013-09-21 15:08:44 +04:00
static const struct of_device_id ohci_hcd_nxp_match [ ] = {
2012-04-29 18:47:03 +04:00
{ . compatible = " nxp,ohci-nxp " } ,
{ } ,
} ;
2013-09-21 15:08:44 +04:00
MODULE_DEVICE_TABLE ( of , ohci_hcd_nxp_match ) ;
2012-04-29 18:47:03 +04:00
# endif
2013-09-21 15:08:44 +04:00
static struct platform_driver ohci_hcd_nxp_driver = {
2006-06-29 18:28:18 +04:00
. driver = {
. name = " usb-ohci " ,
2013-09-21 15:08:44 +04:00
. of_match_table = of_match_ptr ( ohci_hcd_nxp_match ) ,
2006-06-29 18:28:18 +04:00
} ,
2013-09-21 15:08:44 +04:00
. probe = ohci_hcd_nxp_probe ,
2023-05-18 02:02:09 +03:00
. remove_new = ohci_hcd_nxp_remove ,
2006-06-29 18:28:18 +04:00
} ;
2013-09-21 15:08:44 +04:00
static int __init ohci_nxp_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
ohci_init_driver ( & ohci_nxp_hc_driver , NULL ) ;
return platform_driver_register ( & ohci_hcd_nxp_driver ) ;
}
module_init ( ohci_nxp_init ) ;
static void __exit ohci_nxp_cleanup ( void )
{
platform_driver_unregister ( & ohci_hcd_nxp_driver ) ;
}
module_exit ( ohci_nxp_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL v2 " ) ;