// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2007,2008 Freescale semiconductor, Inc.
*
* Author: Li Yang <LeoLi@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
*
* Initialization based on code from Shlomi Gridish.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/timer.h>
#include <linux/usb.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/workqueue.h>
#include <linux/time.h>
#include <linux/fsl_devices.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include "phy-fsl-usb.h"
#ifdef VERBOSE
#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
__func__, ## args)
#else
#define VDBG(stuff...) do {} while (0)
#endif
#define DRIVER_VERSION "Rev. 1.55"
#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION
static const char driver_name[] = "fsl-usb2-otg";
const pm_message_t otg_suspend_state = {
.event = 1,
};
#define HA_DATA_PULSE
static struct usb_dr_mmap *usb_dr_regs;
static struct fsl_otg *fsl_otg_dev;
static int srp_wait_done;
/* FSM timers */
struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
*b_ase0_brst_tmr, *b_se0_srp_tmr;
/* Driver specific timers */
struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
*b_srp_wait_tmr, *a_wait_enum_tmr;
static struct list_head active_timers;
static struct fsl_otg_config fsl_otg_initdata = {
.otg_port = 1,
};
#ifdef CONFIG_PPC32
static u32 _fsl_readl_be(const unsigned __iomem *p)
{
return in_be32(p);
}
static u32 _fsl_readl_le(const unsigned __iomem *p)
{
return in_le32(p);
}
static void _fsl_writel_be(u32 v, unsigned __iomem *p)
{
out_be32(p, v);
}
static void _fsl_writel_le(u32 v, unsigned __iomem *p)
{
out_le32(p, v);
}
static u32 (*_fsl_readl)(const unsigned __iomem *p);
static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
#define fsl_readl(p) (*_fsl_readl)((p))
#define fsl_writel(v, p) (*_fsl_writel)((v), (p))
#else
#define fsl_readl(addr) readl(addr)
#define fsl_writel(val, addr) writel(val, addr)
#endif /* CONFIG_PPC32 */
int write_ulpi(u8 addr, u8 data)
{
u32 temp;
temp = 0x60000000 | (addr << 16) | data;
fsl_writel(temp, &usb_dr_regs->ulpiview);
return 0;
}
/* -------------------------------------------------------------*/
/* Operations that will be called from OTG Finite State Machine */
/* Charge vbus for vbus pulsing in SRP */
void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
{
u32 tmp;
tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
if (on)
/* stop discharging, start charging */
tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) |
OTGSC_CTRL_VBUS_CHARGE;
else
/* stop charging */
tmp &= ~OTGSC_CTRL_VBUS_CHARGE;
fsl_writel(tmp, &usb_dr_regs->otgsc);
}
/* Discharge vbus through a resistor to ground */
void fsl_otg_dischrg_vbus(int on)
{
u32 tmp;
tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
if (on)
/* stop charging, start discharging */
tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) |
OTGSC_CTRL_VBUS_DISCHARGE;
else
/* stop discharging */
tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE;
fsl_writel(tmp, &usb_dr_regs->otgsc);
}
/* A-device driver vbus, controlled through PP bit in PORTSC */
void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
{
u32 tmp;
if (on) {
tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS;
fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc);
} else {
tmp = fsl_readl(&usb_dr_regs->portsc) &
~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER;
fsl_writel(tmp, &am