2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0+
2013-12-20 00:42:26 +04:00
/*
* Ingenic JZ4740 " glue layer "
*
* Copyright ( C ) 2013 , Apelete Seketeli < apelete @ seketeli . net >
*/
# include <linux/clk.h>
# include <linux/dma-mapping.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
2019-02-11 20:14:48 +03:00
# include <linux/of_device.h>
2013-12-20 00:42:26 +04:00
# include <linux/platform_device.h>
2020-03-17 00:11:32 +03:00
# include <linux/usb/role.h>
2014-07-06 21:58:03 +04:00
# include <linux/usb/usb_phy_generic.h>
2013-12-20 00:42:26 +04:00
# include "musb_core.h"
struct jz4740_glue {
2020-01-15 16:25:36 +03:00
struct platform_device * pdev ;
2020-03-17 00:11:32 +03:00
struct musb * musb ;
2013-12-20 00:42:26 +04:00
struct clk * clk ;
2020-03-17 00:11:32 +03:00
struct usb_role_switch * role_sw ;
2013-12-20 00:42:26 +04:00
} ;
static irqreturn_t jz4740_musb_interrupt ( int irq , void * __hci )
{
2020-01-15 16:25:38 +03:00
unsigned long flags ;
2020-03-17 00:11:31 +03:00
irqreturn_t retval = IRQ_NONE , retval_dma = IRQ_NONE ;
2020-01-15 16:25:38 +03:00
struct musb * musb = __hci ;
2013-12-20 00:42:26 +04:00
2020-03-17 00:11:31 +03:00
if ( IS_ENABLED ( CONFIG_USB_INVENTRA_DMA ) & & musb - > dma_controller )
retval_dma = dma_controller_irq ( irq , musb - > dma_controller ) ;
2020-05-25 05:50:48 +03:00
spin_lock_irqsave ( & musb - > lock , flags ) ;
2013-12-20 00:42:26 +04:00
musb - > int_usb = musb_readb ( musb - > mregs , MUSB_INTRUSB ) ;
musb - > int_tx = musb_readw ( musb - > mregs , MUSB_INTRTX ) ;
musb - > int_rx = musb_readw ( musb - > mregs , MUSB_INTRRX ) ;
/*
* The controller is gadget only , the state of the host mode IRQ bits is
* undefined . Mask them to make sure that the musb driver core will
* never see them set
*/
musb - > int_usb & = MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
2020-01-15 16:25:38 +03:00
MUSB_INTR_RESET | MUSB_INTR_SOF ;
2013-12-20 00:42:26 +04:00
if ( musb - > int_usb | | musb - > int_tx | | musb - > int_rx )
retval = musb_interrupt ( musb ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
2020-03-17 00:11:31 +03:00
if ( retval = = IRQ_HANDLED | | retval_dma = = IRQ_HANDLED )
return IRQ_HANDLED ;
return IRQ_NONE ;
2013-12-20 00:42:26 +04:00
}
static struct musb_fifo_cfg jz4740_musb_fifo_cfg [ ] = {
2020-01-15 16:25:38 +03:00
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_TX , . maxpacket = 64 , } ,
2013-12-20 00:42:26 +04:00
} ;
2017-01-24 22:22:22 +03:00
static const struct musb_hdrc_config jz4740_musb_config = {
2013-12-20 00:42:26 +04:00
/* Silicon does not implement USB OTG. */
2020-01-15 16:25:38 +03:00
. multipoint = 0 ,
2013-12-20 00:42:26 +04:00
/* Max EPs scanned, driver will decide which EP can be used. */
2020-01-15 16:25:38 +03:00
. num_eps = 4 ,
2013-12-20 00:42:26 +04:00
/* RAMbits needed to configure EPs from table */
2020-01-15 16:25:38 +03:00
. ram_bits = 9 ,
. fifo_cfg = jz4740_musb_fifo_cfg ,
. fifo_cfg_size = ARRAY_SIZE ( jz4740_musb_fifo_cfg ) ,
2013-12-20 00:42:26 +04:00
} ;
2020-03-17 00:11:32 +03:00
static int jz4740_musb_role_switch_set ( struct usb_role_switch * sw ,
enum usb_role role )
{
struct jz4740_glue * glue = usb_role_switch_get_drvdata ( sw ) ;
struct usb_phy * phy = glue - > musb - > xceiv ;
switch ( role ) {
case USB_ROLE_NONE :
atomic_notifier_call_chain ( & phy - > notifier , USB_EVENT_NONE , phy ) ;
break ;
case USB_ROLE_DEVICE :
atomic_notifier_call_chain ( & phy - > notifier , USB_EVENT_VBUS , phy ) ;
break ;
case USB_ROLE_HOST :
atomic_notifier_call_chain ( & phy - > notifier , USB_EVENT_ID , phy ) ;
break ;
}
return 0 ;
}
2013-12-20 00:42:26 +04:00
static int jz4740_musb_init ( struct musb * musb )
{
2019-04-30 17:59:40 +03:00
struct device * dev = musb - > controller - > parent ;
2020-03-17 00:11:32 +03:00
struct jz4740_glue * glue = dev_get_drvdata ( dev ) ;
struct usb_role_switch_desc role_sw_desc = {
. set = jz4740_musb_role_switch_set ,
. driver_data = glue ,
. fwnode = dev_fwnode ( dev ) ,
} ;
2019-12-16 19:24:32 +03:00
int err ;
2019-04-30 17:59:40 +03:00
2020-03-17 00:11:32 +03:00
glue - > musb = musb ;
2019-04-30 17:59:40 +03:00
if ( dev - > of_node )
musb - > xceiv = devm_usb_get_phy_by_phandle ( dev , " phys " , 0 ) ;
else
musb - > xceiv = devm_usb_get_phy ( dev , USB_PHY_TYPE_USB2 ) ;
2016-04-25 23:53:31 +03:00
if ( IS_ERR ( musb - > xceiv ) ) {
2019-12-16 19:24:32 +03:00
err = PTR_ERR ( musb - > xceiv ) ;
if ( err ! = - EPROBE_DEFER )
dev_err ( dev , " No transceiver configured: %d " , err ) ;
return err ;
2013-12-20 00:42:26 +04:00
}
2020-03-17 00:11:32 +03:00
glue - > role_sw = usb_role_switch_register ( dev , & role_sw_desc ) ;
if ( IS_ERR ( glue - > role_sw ) ) {
dev_err ( dev , " Failed to register USB role switch " ) ;
return PTR_ERR ( glue - > role_sw ) ;
}
2020-01-15 16:25:37 +03:00
/*
* Silicon does not implement ConfigData register .
2013-12-20 00:42:26 +04:00
* Set dyn_fifo to avoid reading EP config from hardware .
*/
musb - > dyn_fifo = true ;
musb - > isr = jz4740_musb_interrupt ;
return 0 ;
}
2020-03-17 00:11:32 +03:00
static int jz4740_musb_exit ( struct musb * musb )
{
struct jz4740_glue * glue = dev_get_drvdata ( musb - > controller - > parent ) ;
usb_role_switch_unregister ( glue - > role_sw ) ;
return 0 ;
}
2013-12-20 00:42:26 +04:00
static const struct musb_platform_ops jz4740_musb_ops = {
2015-05-01 22:29:27 +03:00
. quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP ,
2014-11-24 22:05:04 +03:00
. fifo_mode = 2 ,
2013-12-20 00:42:26 +04:00
. init = jz4740_musb_init ,
2020-03-17 00:11:32 +03:00
. exit = jz4740_musb_exit ,
2020-03-17 00:11:31 +03:00
# ifdef CONFIG_USB_INVENTRA_DMA
. dma_init = musbhs_dma_controller_create_noirq ,
. dma_exit = musbhs_dma_controller_destroy ,
# endif
2013-12-20 00:42:26 +04:00
} ;
2020-01-15 16:25:35 +03:00
static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
. mode = MUSB_PERIPHERAL ,
. config = & jz4740_musb_config ,
. platform_ops = & jz4740_musb_ops ,
} ;
2020-03-17 00:11:34 +03:00
static struct musb_fifo_cfg jz4770_musb_fifo_cfg [ ] = {
{ . hw_ep_num = 1 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 1 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 2 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 3 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 3 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 4 , . style = FIFO_RX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_TX , . maxpacket = 512 , } ,
{ . hw_ep_num = 5 , . style = FIFO_RX , . maxpacket = 512 , } ,
} ;
static struct musb_hdrc_config jz4770_musb_config = {
. multipoint = 1 ,
. num_eps = 11 ,
. ram_bits = 11 ,
. fifo_cfg = jz4770_musb_fifo_cfg ,
. fifo_cfg_size = ARRAY_SIZE ( jz4770_musb_fifo_cfg ) ,
} ;
static const struct musb_hdrc_platform_data jz4770_musb_pdata = {
. mode = MUSB_PERIPHERAL , /* TODO: support OTG */
. config = & jz4770_musb_config ,
. platform_ops = & jz4740_musb_ops ,
} ;
2013-12-20 00:42:26 +04:00
static int jz4740_probe ( struct platform_device * pdev )
{
2020-01-15 16:25:34 +03:00
struct device * dev = & pdev - > dev ;
2020-03-17 00:11:33 +03:00
const struct musb_hdrc_platform_data * pdata ;
2013-12-20 00:42:26 +04:00
struct platform_device * musb ;
struct jz4740_glue * glue ;
2020-01-15 16:25:38 +03:00
struct clk * clk ;
2013-12-20 00:42:26 +04:00
int ret ;
2020-01-15 16:25:34 +03:00
glue = devm_kzalloc ( dev , sizeof ( * glue ) , GFP_KERNEL ) ;
2013-12-20 00:42:26 +04:00
if ( ! glue )
return - ENOMEM ;
2020-03-17 00:11:33 +03:00
pdata = of_device_get_match_data ( dev ) ;
if ( ! pdata ) {
dev_err ( dev , " missing platform data " ) ;
return - EINVAL ;
}
2013-12-20 00:42:26 +04:00
musb = platform_device_alloc ( " musb-hdrc " , PLATFORM_DEVID_AUTO ) ;
if ( ! musb ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to allocate musb device " ) ;
2013-12-20 00:42:26 +04:00
return - ENOMEM ;
}
2020-01-15 16:25:34 +03:00
clk = devm_clk_get ( dev , " udc " ) ;
2013-12-20 00:42:26 +04:00
if ( IS_ERR ( clk ) ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to get clock " ) ;
2013-12-20 00:42:26 +04:00
ret = PTR_ERR ( clk ) ;
goto err_platform_device_put ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to enable clock " ) ;
2013-12-20 00:42:26 +04:00
goto err_platform_device_put ;
}
2020-01-15 16:25:34 +03:00
musb - > dev . parent = dev ;
2020-03-17 00:11:31 +03:00
musb - > dev . dma_mask = & musb - > dev . coherent_dma_mask ;
musb - > dev . coherent_dma_mask = DMA_BIT_MASK ( 32 ) ;
2013-12-20 00:42:26 +04:00
2020-01-15 16:25:36 +03:00
glue - > pdev = musb ;
2013-12-20 00:42:26 +04:00
glue - > clk = clk ;
platform_set_drvdata ( pdev , glue ) ;
ret = platform_device_add_resources ( musb , pdev - > resource ,
pdev - > num_resources ) ;
if ( ret ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to add resources " ) ;
2013-12-20 00:42:26 +04:00
goto err_clk_disable ;
}
ret = platform_device_add_data ( musb , pdata , sizeof ( * pdata ) ) ;
if ( ret ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to add platform_data " ) ;
2013-12-20 00:42:26 +04:00
goto err_clk_disable ;
}
ret = platform_device_add ( musb ) ;
if ( ret ) {
2020-01-15 16:25:34 +03:00
dev_err ( dev , " failed to register musb device " ) ;
2013-12-20 00:42:26 +04:00
goto err_clk_disable ;
}
return 0 ;
err_clk_disable :
clk_disable_unprepare ( clk ) ;
err_platform_device_put :
platform_device_put ( musb ) ;
return ret ;
}
static int jz4740_remove ( struct platform_device * pdev )
{
2020-01-15 16:25:38 +03:00
struct jz4740_glue * glue = platform_get_drvdata ( pdev ) ;
2013-12-20 00:42:26 +04:00
2020-01-15 16:25:36 +03:00
platform_device_unregister ( glue - > pdev ) ;
2013-12-20 00:42:26 +04:00
clk_disable_unprepare ( glue - > clk ) ;
return 0 ;
}
2019-02-11 20:14:48 +03:00
static const struct of_device_id jz4740_musb_of_match [ ] = {
2020-03-17 00:11:33 +03:00
{ . compatible = " ingenic,jz4740-musb " , . data = & jz4740_musb_pdata } ,
2020-03-17 00:11:34 +03:00
{ . compatible = " ingenic,jz4770-musb " , . data = & jz4770_musb_pdata } ,
2020-01-15 16:25:37 +03:00
{ /* sentinel */ } ,
2019-02-11 20:14:48 +03:00
} ;
MODULE_DEVICE_TABLE ( of , jz4740_musb_of_match ) ;
2013-12-20 00:42:26 +04:00
static struct platform_driver jz4740_driver = {
. probe = jz4740_probe ,
. remove = jz4740_remove ,
. driver = {
. name = " musb-jz4740 " ,
2020-03-17 00:11:33 +03:00
. of_match_table = jz4740_musb_of_match ,
2013-12-20 00:42:26 +04:00
} ,
} ;
MODULE_DESCRIPTION ( " JZ4740 MUSB Glue Layer " ) ;
MODULE_AUTHOR ( " Apelete Seketeli <apelete@seketeli.net> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
module_platform_driver ( jz4740_driver ) ;