Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66ee3bef authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Paul Mundt
Browse files

ARM: mach-shmobile: mackerel: add renesas_usbhs support for USB1



renesas_usbhs driver can use external interrupt mode
(which come from USB-PHY) or autonomy mode (it use own interrupt)
for detecting connection/disconnection when Function.
And it will be power OFF while it has been disconnecting
if external interrupt mode is selected.

mackerel board has 2 USB ports.

But we can not use external interrupt mode
on CN22 USB0 port which is only for USB Function.
IRQ7-PORT40 is already used by Touchscreen,
and USB-PHY needs IRQ7-PORT167.
It is impossible to use IRQ7 demux on mackerel.

We can use external interrupt mode USB-Function on "USB1".
USB1 can become Host by r8a66597, and become Function by renesas_usbhs.
But don't select both drivers in same time.
These 2 drivers are not supporting IRQ SHARD.

Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 1acd3de5
Loading
Loading
Loading
Loading
+190 −1
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <linux/sh_intc.h>
#include <linux/tca6416_keypad.h>
#include <linux/usb/r8a66597.h>
#include <linux/usb/renesas_usbhs.h>

#include <video/sh_mobile_hdmi.h>
#include <video/sh_mobile_lcdc.h>
@@ -143,7 +144,30 @@
 * open      | external VBUS | Function
 *
 * *1
 * CN31 is used as Host in Linux.
 * CN31 is used as
 * CONFIG_USB_R8A66597_HCD	Host
 * CONFIG_USB_RENESAS_USBHS	Function
 *
 * CAUTION
 *
 * renesas_usbhs driver can use external interrupt mode
 * (which come from USB-PHY) or autonomy mode (it use own interrupt)
 * for detecting connection/disconnection when Function.
 * USB will be power OFF while it has been disconnecting
 * if external interrupt mode, and it is always power ON if autonomy mode,
 *
 * mackerel can not use external interrupt (IRQ7-PORT167) mode on "USB0",
 * because Touchscreen is using IRQ7-PORT40.
 * It is impossible to use IRQ7 demux on this board.
 *
 * We can use external interrupt mode USB-Function on "USB1".
 * USB1 can become Host by r8a66597, and become Function by renesas_usbhs.
 * But don't select both drivers in same time.
 * These uses same IRQ number for request_irq(), and aren't supporting
 * IRQF_SHARD / IORESOURCE_IRQ_SHAREABLE.
 *
 * Actually these are old/new version of USB driver.
 * This mean its register will be broken if it supports SHARD IRQ,
 */

/*
@@ -185,6 +209,7 @@
 * FIXME !!
 *
 * gpio_no_direction
 * gpio_pull_down
 * are quick_hack.
 *
 * current gpio frame work doesn't have
@@ -196,6 +221,16 @@ static void __init gpio_no_direction(u32 addr)
	__raw_writeb(0x00, addr);
}

static void __init gpio_pull_down(u32 addr)
{
	u8 data = __raw_readb(addr);

	data &= 0x0F;
	data |= 0xA0;

	__raw_writeb(data, addr);
}

/* MTD */
static struct mtd_partition nor_flash_partitions[] = {
	{
@@ -509,6 +544,157 @@ static struct platform_device usb1_host_device = {
	.resource	= usb1_host_resources,
};

/* USB1 (Function) */
#define USB_PHY_MODE		(1 << 4)
#define USB_PHY_INT_EN		((1 << 3) | (1 << 2))
#define USB_PHY_ON		(1 << 1)
#define USB_PHY_OFF		(1 << 0)
#define USB_PHY_INT_CLR		(USB_PHY_ON | USB_PHY_OFF)

struct usbhs_private {
	unsigned int irq;
	unsigned int usbphyaddr;
	unsigned int usbcrcaddr;
	struct renesas_usbhs_platform_info info;
};

#define usbhs_get_priv(pdev)				\
	container_of(renesas_usbhs_get_info(pdev),	\
		     struct usbhs_private, info)

#define usbhs_is_connected(priv)			\
	(!((1 << 7) & __raw_readw(priv->usbcrcaddr)))

static int usbhs1_get_id(struct platform_device *pdev)
{
	return USBHS_GADGET;
}

static int usbhs1_get_vbus(struct platform_device *pdev)
{
	return usbhs_is_connected(usbhs_get_priv(pdev));
}

static irqreturn_t usbhs1_interrupt(int irq, void *data)
{
	struct platform_device *pdev = data;
	struct usbhs_private *priv = usbhs_get_priv(pdev);

	dev_dbg(&pdev->dev, "%s\n", __func__);

	renesas_usbhs_call_notify_hotplug(pdev);

	/* clear status */
	__raw_writew(__raw_readw(priv->usbphyaddr) | USB_PHY_INT_CLR,
		     priv->usbphyaddr);

	return IRQ_HANDLED;
}

static int usbhs1_hardware_init(struct platform_device *pdev)
{
	struct usbhs_private *priv = usbhs_get_priv(pdev);
	int ret;

	irq_set_irq_type(priv->irq, IRQ_TYPE_LEVEL_HIGH);

	/* clear interrupt status */
	__raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr);

	ret = request_irq(priv->irq, usbhs1_interrupt, 0,
			  dev_name(&pdev->dev), pdev);
	if (ret) {
		dev_err(&pdev->dev, "request_irq err\n");
		return ret;
	}

	/* enable USB phy interrupt */
	__raw_writew(USB_PHY_MODE | USB_PHY_INT_EN, priv->usbphyaddr);

	return 0;
}

static void usbhs1_hardware_exit(struct platform_device *pdev)
{
	struct usbhs_private *priv = usbhs_get_priv(pdev);

	/* clear interrupt status */
	__raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr);

	free_irq(priv->irq, pdev);
}

static void usbhs1_phy_reset(struct platform_device *pdev)
{
	struct usbhs_private *priv = usbhs_get_priv(pdev);

	/* init phy */
	__raw_writew(0x8a0a, priv->usbcrcaddr);
}

static u32 usbhs1_pipe_cfg[] = {
	USB_ENDPOINT_XFER_CONTROL,
	USB_ENDPOINT_XFER_ISOC,
	USB_ENDPOINT_XFER_ISOC,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_INT,
	USB_ENDPOINT_XFER_INT,
	USB_ENDPOINT_XFER_INT,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
	USB_ENDPOINT_XFER_BULK,
};

static struct usbhs_private usbhs1_private = {
	.irq		= evt2irq(0x0300),	/* IRQ8 */
	.usbphyaddr	= 0xE60581E2,		/* USBPHY1INTAP */
	.usbcrcaddr	= 0xE6058130,		/* USBCR4 */
	.info = {
		.platform_callback = {
			.hardware_init	= usbhs1_hardware_init,
			.hardware_exit	= usbhs1_hardware_exit,
			.phy_reset	= usbhs1_phy_reset,
			.get_id		= usbhs1_get_id,
			.get_vbus	= usbhs1_get_vbus,
		},
		.driver_param = {
			.buswait_bwait	= 4,
			.pipe_type	= usbhs1_pipe_cfg,
			.pipe_size	= ARRAY_SIZE(usbhs1_pipe_cfg),
		},
	},
};

static struct resource usbhs1_resources[] = {
	[0] = {
		.name	= "USBHS",
		.start	= 0xE68B0000,
		.end	= 0xE68B00E6 - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= evt2irq(0x1ce0) /* USB1_USB1I0 */,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct platform_device usbhs1_device = {
	.name	= "renesas_usbhs",
	.id	= 1,
	.dev = {
		.platform_data		= &usbhs1_private.info,
	},
	.num_resources	= ARRAY_SIZE(usbhs1_resources),
	.resource	= usbhs1_resources,
};


/* LED */
static struct gpio_led mackerel_leds[] = {
	{
@@ -949,6 +1135,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
	&smc911x_device,
	&lcdc_device,
	&usb1_host_device,
	&usbhs1_device,
	&leds_device,
	&fsi_device,
	&fsi_ak4643_device,
@@ -1044,6 +1231,7 @@ static void __init mackerel_map_io(void)

#define GPIO_PORT9CR	0xE6051009
#define GPIO_PORT10CR	0xE605100A
#define GPIO_PORT168CR	0xE60520A8
#define SRCR4		0xe61580bc
#define USCCR1		0xE6058144
static void __init mackerel_init(void)
@@ -1102,6 +1290,7 @@ static void __init mackerel_init(void)
	gpio_request(GPIO_FN_OVCN_1_114, NULL);
	gpio_request(GPIO_FN_EXTLP_1,    NULL);
	gpio_request(GPIO_FN_OVCN2_1,    NULL);
	gpio_pull_down(GPIO_PORT168CR);

	/* setup USB phy */
	__raw_writew(0x8a0a, 0xE6058130);	/* USBCR4 */