mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
e6b5be2be4
Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlSOD20ACgkQMUfUDdst+ylLPACg2QrW1oHhdTMT9WI8jihlHVRM 53kAoLeteByQ3iVwWurwwseRPiWa8+MI =OVRS -----END PGP SIGNATURE----- Merge tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core update from Greg KH: "Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while" * tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (324 commits) Revert "ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries" fs: debugfs: add forward declaration for struct device type firmware class: Deletion of an unnecessary check before the function call "vunmap" firmware loader: fix hung task warning dump devcoredump: provide a one-way disable function device: Add dev_<level>_once variants ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries ath: use seq_file api for ath9k debugfs files debugfs: add helper function to create device related seq_file drivers/base: cacheinfo: remove noisy error boot message Revert "core: platform: add warning if driver has no owner" drivers: base: support cpu cache information interface to userspace via sysfs drivers: base: add cpu_device_create to support per-cpu devices topology: replace custom attribute macros with standard DEVICE_ATTR* cpumask: factor out show_cpumap into separate helper function driver core: Fix unbalanced device reference in drivers_probe driver core: fix race with userland in device_add() sysfs/kernfs: make read requests on pre-alloc files use the buffer. sysfs/kernfs: allow attributes to request write buffer be pre-allocated. fs: sysfs: return EGBIG on write if offset is larger than file size ...
391 lines
8.2 KiB
C
391 lines
8.2 KiB
C
/*
|
|
* linux/arch/arm/mach-pxa/pxa3xx-ulpi.c
|
|
*
|
|
* code specific to pxa3xx aka Monahans
|
|
*
|
|
* Copyright (C) 2010 CompuLab Ltd.
|
|
*
|
|
* 2010-13-07: Igor Grinberg <grinberg@compulab.co.il>
|
|
* initial version: pxa310 USB Host mode support
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/otg.h>
|
|
|
|
#include <mach/hardware.h>
|
|
#include <mach/regs-u2d.h>
|
|
#include <linux/platform_data/usb-pxa3xx-ulpi.h>
|
|
|
|
struct pxa3xx_u2d_ulpi {
|
|
struct clk *clk;
|
|
void __iomem *mmio_base;
|
|
|
|
struct usb_phy *otg;
|
|
unsigned int ulpi_mode;
|
|
};
|
|
|
|
static struct pxa3xx_u2d_ulpi *u2d;
|
|
|
|
static inline u32 u2d_readl(u32 reg)
|
|
{
|
|
return __raw_readl(u2d->mmio_base + reg);
|
|
}
|
|
|
|
static inline void u2d_writel(u32 reg, u32 val)
|
|
{
|
|
__raw_writel(val, u2d->mmio_base + reg);
|
|
}
|
|
|
|
#if defined(CONFIG_PXA310_ULPI)
|
|
enum u2d_ulpi_phy_mode {
|
|
SYNCH = 0,
|
|
CARKIT = (1 << 0),
|
|
SER_3PIN = (1 << 1),
|
|
SER_6PIN = (1 << 2),
|
|
LOWPOWER = (1 << 3),
|
|
};
|
|
|
|
static inline enum u2d_ulpi_phy_mode pxa310_ulpi_get_phymode(void)
|
|
{
|
|
return (u2d_readl(U2DOTGUSR) >> 28) & 0xF;
|
|
}
|
|
|
|
static int pxa310_ulpi_poll(void)
|
|
{
|
|
int timeout = 50000;
|
|
|
|
while (timeout--) {
|
|
if (!(u2d_readl(U2DOTGUCR) & U2DOTGUCR_RUN))
|
|
return 0;
|
|
|
|
cpu_relax();
|
|
}
|
|
|
|
pr_warn("%s: ULPI access timed out!\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg)
|
|
{
|
|
int err;
|
|
|
|
if (pxa310_ulpi_get_phymode() != SYNCH) {
|
|
pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
|
|
u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << 16));
|
|
msleep(5);
|
|
|
|
err = pxa310_ulpi_poll();
|
|
if (err)
|
|
return err;
|
|
|
|
return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA;
|
|
}
|
|
|
|
static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
|
|
{
|
|
if (pxa310_ulpi_get_phymode() != SYNCH) {
|
|
pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
|
|
u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | (reg << 16) | (val << 8));
|
|
msleep(5);
|
|
|
|
return pxa310_ulpi_poll();
|
|
}
|
|
|
|
struct usb_phy_io_ops pxa310_ulpi_access_ops = {
|
|
.read = pxa310_ulpi_read,
|
|
.write = pxa310_ulpi_write,
|
|
};
|
|
|
|
static void pxa310_otg_transceiver_rtsm(void)
|
|
{
|
|
u32 u2dotgcr;
|
|
|
|
/* put PHY to sync mode */
|
|
u2dotgcr = u2d_readl(U2DOTGCR);
|
|
u2dotgcr |= U2DOTGCR_RTSM | U2DOTGCR_UTMID;
|
|
u2d_writel(U2DOTGCR, u2dotgcr);
|
|
msleep(10);
|
|
|
|
/* setup OTG sync mode */
|
|
u2dotgcr = u2d_readl(U2DOTGCR);
|
|
u2dotgcr |= U2DOTGCR_ULAF;
|
|
u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
|
|
u2d_writel(U2DOTGCR, u2dotgcr);
|
|
}
|
|
|
|
static int pxa310_start_otg_host_transcvr(struct usb_bus *host)
|
|
{
|
|
int err;
|
|
|
|
pxa310_otg_transceiver_rtsm();
|
|
|
|
err = usb_phy_init(u2d->otg);
|
|
if (err) {
|
|
pr_err("OTG transceiver init failed");
|
|
return err;
|
|
}
|
|
|
|
err = otg_set_vbus(u2d->otg->otg, 1);
|
|
if (err) {
|
|
pr_err("OTG transceiver VBUS set failed");
|
|
return err;
|
|
}
|
|
|
|
err = otg_set_host(u2d->otg->otg, host);
|
|
if (err)
|
|
pr_err("OTG transceiver Host mode set failed");
|
|
|
|
return err;
|
|
}
|
|
|
|
static int pxa310_start_otg_hc(struct usb_bus *host)
|
|
{
|
|
u32 u2dotgcr;
|
|
int err;
|
|
|
|
/* disable USB device controller */
|
|
u2d_writel(U2DCR, u2d_readl(U2DCR) & ~U2DCR_UDE);
|
|
u2d_writel(U2DOTGCR, u2d_readl(U2DOTGCR) | U2DOTGCR_UTMID);
|
|
u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);
|
|
|
|
err = pxa310_start_otg_host_transcvr(host);
|
|
if (err)
|
|
return err;
|
|
|
|
/* set xceiver mode */
|
|
if (u2d->ulpi_mode & ULPI_IC_6PIN_SERIAL)
|
|
u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) & ~U2DP3CR_P2SS);
|
|
else if (u2d->ulpi_mode & ULPI_IC_3PIN_SERIAL)
|
|
u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) | U2DP3CR_P2SS);
|
|
|
|
/* start OTG host controller */
|
|
u2dotgcr = u2d_readl(U2DOTGCR) | U2DOTGCR_SMAF;
|
|
u2d_writel(U2DOTGCR, u2dotgcr & ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pxa310_stop_otg_hc(void)
|
|
{
|
|
pxa310_otg_transceiver_rtsm();
|
|
|
|
otg_set_host(u2d->otg->otg, NULL);
|
|
otg_set_vbus(u2d->otg->otg, 0);
|
|
usb_phy_shutdown(u2d->otg);
|
|
}
|
|
|
|
static void pxa310_u2d_setup_otg_hc(void)
|
|
{
|
|
u32 u2dotgcr;
|
|
|
|
u2dotgcr = u2d_readl(U2DOTGCR);
|
|
u2dotgcr |= U2DOTGCR_ULAF | U2DOTGCR_UTMID;
|
|
u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
|
|
u2d_writel(U2DOTGCR, u2dotgcr);
|
|
msleep(5);
|
|
u2d_writel(U2DOTGCR, u2dotgcr | U2DOTGCR_ULE);
|
|
msleep(5);
|
|
u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);
|
|
}
|
|
|
|
static int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
|
|
{
|
|
unsigned int ulpi_mode = ULPI_OTG_DRVVBUS;
|
|
|
|
if (pdata) {
|
|
if (pdata->ulpi_mode & ULPI_SER_6PIN)
|
|
ulpi_mode |= ULPI_IC_6PIN_SERIAL;
|
|
else if (pdata->ulpi_mode & ULPI_SER_3PIN)
|
|
ulpi_mode |= ULPI_IC_3PIN_SERIAL;
|
|
}
|
|
|
|
u2d->ulpi_mode = ulpi_mode;
|
|
|
|
u2d->otg = otg_ulpi_create(&pxa310_ulpi_access_ops, ulpi_mode);
|
|
if (!u2d->otg)
|
|
return -ENOMEM;
|
|
|
|
u2d->otg->io_priv = u2d->mmio_base;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pxa310_otg_exit(void)
|
|
{
|
|
kfree(u2d->otg);
|
|
}
|
|
#else
|
|
static inline void pxa310_u2d_setup_otg_hc(void) {}
|
|
static inline int pxa310_start_otg_hc(struct usb_bus *host)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void pxa310_stop_otg_hc(void) {}
|
|
static inline int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void pxa310_otg_exit(void) {}
|
|
#endif /* CONFIG_PXA310_ULPI */
|
|
|
|
int pxa3xx_u2d_start_hc(struct usb_bus *host)
|
|
{
|
|
int err = 0;
|
|
|
|
/* In case the PXA3xx ULPI isn't used, do nothing. */
|
|
if (!u2d)
|
|
return 0;
|
|
|
|
clk_enable(u2d->clk);
|
|
|
|
if (cpu_is_pxa310()) {
|
|
pxa310_u2d_setup_otg_hc();
|
|
err = pxa310_start_otg_hc(host);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(pxa3xx_u2d_start_hc);
|
|
|
|
void pxa3xx_u2d_stop_hc(struct usb_bus *host)
|
|
{
|
|
/* In case the PXA3xx ULPI isn't used, do nothing. */
|
|
if (!u2d)
|
|
return;
|
|
|
|
if (cpu_is_pxa310())
|
|
pxa310_stop_otg_hc();
|
|
|
|
clk_disable(u2d->clk);
|
|
}
|
|
EXPORT_SYMBOL_GPL(pxa3xx_u2d_stop_hc);
|
|
|
|
static int pxa3xx_u2d_probe(struct platform_device *pdev)
|
|
{
|
|
struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
|
|
struct resource *r;
|
|
int err;
|
|
|
|
u2d = kzalloc(sizeof(struct pxa3xx_u2d_ulpi), GFP_KERNEL);
|
|
if (!u2d) {
|
|
dev_err(&pdev->dev, "failed to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
u2d->clk = clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(u2d->clk)) {
|
|
dev_err(&pdev->dev, "failed to get u2d clock\n");
|
|
err = PTR_ERR(u2d->clk);
|
|
goto err_free_mem;
|
|
}
|
|
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!r) {
|
|
dev_err(&pdev->dev, "no IO memory resource defined\n");
|
|
err = -ENODEV;
|
|
goto err_put_clk;
|
|
}
|
|
|
|
r = request_mem_region(r->start, resource_size(r), pdev->name);
|
|
if (!r) {
|
|
dev_err(&pdev->dev, "failed to request memory resource\n");
|
|
err = -EBUSY;
|
|
goto err_put_clk;
|
|
}
|
|
|
|
u2d->mmio_base = ioremap(r->start, resource_size(r));
|
|
if (!u2d->mmio_base) {
|
|
dev_err(&pdev->dev, "ioremap() failed\n");
|
|
err = -ENODEV;
|
|
goto err_free_res;
|
|
}
|
|
|
|
if (pdata->init) {
|
|
err = pdata->init(&pdev->dev);
|
|
if (err)
|
|
goto err_free_io;
|
|
}
|
|
|
|
/* Only PXA310 U2D has OTG functionality */
|
|
if (cpu_is_pxa310()) {
|
|
err = pxa310_otg_init(pdata);
|
|
if (err)
|
|
goto err_free_plat;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, &u2d);
|
|
|
|
return 0;
|
|
|
|
err_free_plat:
|
|
if (pdata->exit)
|
|
pdata->exit(&pdev->dev);
|
|
err_free_io:
|
|
iounmap(u2d->mmio_base);
|
|
err_free_res:
|
|
release_mem_region(r->start, resource_size(r));
|
|
err_put_clk:
|
|
clk_put(u2d->clk);
|
|
err_free_mem:
|
|
kfree(u2d);
|
|
return err;
|
|
}
|
|
|
|
static int pxa3xx_u2d_remove(struct platform_device *pdev)
|
|
{
|
|
struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
|
|
struct resource *r;
|
|
|
|
if (cpu_is_pxa310()) {
|
|
pxa310_stop_otg_hc();
|
|
pxa310_otg_exit();
|
|
}
|
|
|
|
if (pdata->exit)
|
|
pdata->exit(&pdev->dev);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
iounmap(u2d->mmio_base);
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
release_mem_region(r->start, resource_size(r));
|
|
|
|
clk_put(u2d->clk);
|
|
|
|
kfree(u2d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver pxa3xx_u2d_ulpi_driver = {
|
|
.driver = {
|
|
.name = "pxa3xx-u2d",
|
|
},
|
|
.probe = pxa3xx_u2d_probe,
|
|
.remove = pxa3xx_u2d_remove,
|
|
};
|
|
module_platform_driver(pxa3xx_u2d_ulpi_driver);
|
|
|
|
MODULE_DESCRIPTION("PXA3xx U2D ULPI driver");
|
|
MODULE_AUTHOR("Igor Grinberg");
|
|
MODULE_LICENSE("GPL v2");
|