mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
[mcs814x] add Moschip MSC814x support
This target currently only supports Moschip's MCS8140 SoC, but support for other chips in the same family (MCS8142, MCS8144) will be easy to add. Target support is entirely using Device Tree for probing peripherals. Drivers support include: - PCI - USB 1 & 2 - watchdog - random number generator - UART - timer - internal Ethernet PHY - Ethernet MAC core Support for the following boards is included using Device Tree - Devolo dLAN USB Extender - Tigal RBT-832 git-svn-id: svn://svn.openwrt.org/openwrt/trunk@32462 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* RNG driver for Moschip MCS814x SoC
|
||||
*
|
||||
* Copyright 2012 (C), Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define STAT 0x00
|
||||
#define RND 0x04
|
||||
|
||||
struct mcs814x_rng_priv {
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
static int mcs814x_rng_data_read(struct hwrng *rng, u32 *buffer)
|
||||
{
|
||||
struct mcs814x_rng_priv *priv = (struct mcs814x_rng_priv *)rng->priv;
|
||||
|
||||
*buffer = __raw_readl(priv->regs + RND);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int mcs814x_rng_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct mcs814x_rng_priv *priv;
|
||||
struct hwrng *rng;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rng = kzalloc(sizeof(*rng), GFP_KERNEL);
|
||||
if (!rng) {
|
||||
ret = -ENOMEM;
|
||||
goto out_priv;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rng);
|
||||
rng->priv = (unsigned long)priv;
|
||||
rng->name = pdev->name;
|
||||
rng->data_read = mcs814x_rng_data_read;
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev,
|
||||
res->start, resource_size(res),
|
||||
pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out_rng;
|
||||
}
|
||||
|
||||
priv->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!priv->regs) {
|
||||
ret = -ENOMEM;
|
||||
goto out_rng;
|
||||
}
|
||||
|
||||
ret = hwrng_register(rng);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register hwrng driver\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "registered\n");
|
||||
|
||||
return ret;
|
||||
|
||||
out_rng:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(rng);
|
||||
out_priv:
|
||||
kfree(priv);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcs814x_rng_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hwrng *rng = platform_get_drvdata(pdev);
|
||||
struct mcs814x_rng_priv *priv = (struct mcs814x_rng_priv *)rng->priv;
|
||||
|
||||
hwrng_unregister(rng);
|
||||
kfree(priv);
|
||||
kfree(rng);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mcs814x_rng_ids[] = {
|
||||
{ .compatible = "moschip,mcs814x-rng", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver mcs814x_rng_driver = {
|
||||
.driver = {
|
||||
.name = "mcs814x-rng",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mcs814x_rng_ids,
|
||||
},
|
||||
.probe = mcs814x_rng_probe,
|
||||
.remove = __devexit_p(mcs814x_rng_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(mcs814x_rng_driver);
|
||||
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_DESCRIPTION("H/W Random Number Generator (RNG) for Moschip MCS814x");
|
||||
MODULE_LICENSE("GPL");
|
||||
148
target/linux/mcs814x/files-3.3/drivers/gpio/gpio-mcs814x.c
Normal file
148
target/linux/mcs814x/files-3.3/drivers/gpio/gpio-mcs814x.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Moschip MCS814x GPIO support
|
||||
*
|
||||
* Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* Licensed under the GPLv2
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
struct mcs814x_gpio_chip {
|
||||
void __iomem *regs;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
#define GPIO_PIN 0x00
|
||||
#define GPIO_DIR 0x04
|
||||
|
||||
#define to_mcs814x_gpio_chip(x) container_of(x, struct mcs814x_gpio_chip, chip)
|
||||
|
||||
static int mcs814x_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
|
||||
|
||||
return __raw_readl(mcs814x->regs + GPIO_PIN) & (1 << offset);
|
||||
}
|
||||
|
||||
static void mcs814x_gpio_set(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
|
||||
u32 mask;
|
||||
|
||||
mask = __raw_readl(mcs814x->regs + GPIO_PIN);
|
||||
if (value)
|
||||
mask |= (1 << offset);
|
||||
else
|
||||
mask &= ~(1 << offset);
|
||||
__raw_writel(mask, mcs814x->regs + GPIO_PIN);
|
||||
}
|
||||
|
||||
static int mcs814x_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
|
||||
u32 mask;
|
||||
|
||||
mask = __raw_readl(mcs814x->regs + GPIO_DIR);
|
||||
mask &= ~(1 << offset);
|
||||
__raw_writel(mask, mcs814x->regs + GPIO_DIR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs814x_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
|
||||
u32 mask;
|
||||
|
||||
mask = __raw_readl(mcs814x->regs + GPIO_DIR);
|
||||
mask |= (1 << offset);
|
||||
__raw_writel(mask, mcs814x->regs + GPIO_DIR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit mcs814x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct mcs814x_gpio_chip *mcs814x_chip;
|
||||
int ret;
|
||||
const unsigned int *num_gpios;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
num_gpios = of_get_property(pdev->dev.of_node, "num-gpios", NULL);
|
||||
if (!num_gpios)
|
||||
dev_err(&pdev->dev, "FIXME: no num-gpios property\n");
|
||||
|
||||
mcs814x_chip = kzalloc(sizeof(*mcs814x_chip), GFP_KERNEL);
|
||||
if (!mcs814x_chip)
|
||||
return -ENOMEM;
|
||||
|
||||
mcs814x_chip->regs = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!mcs814x_chip->regs) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, mcs814x_chip);
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
mcs814x_chip->chip.of_node = pdev->dev.of_node;
|
||||
#endif
|
||||
|
||||
mcs814x_chip->chip.label = pdev->name;
|
||||
mcs814x_chip->chip.get = mcs814x_gpio_get;
|
||||
mcs814x_chip->chip.set = mcs814x_gpio_set;
|
||||
mcs814x_chip->chip.direction_input = mcs814x_gpio_direction_input;
|
||||
mcs814x_chip->chip.direction_output = mcs814x_gpio_direction_output;
|
||||
mcs814x_chip->chip.ngpio = be32_to_cpup(num_gpios);
|
||||
/* we want dynamic base allocation */
|
||||
mcs814x_chip->chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&mcs814x_chip->chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register gpiochip\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(mcs814x_chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct of_device_id mcs814x_gpio_ids[] __devinitdata = {
|
||||
{ .compatible = "moschip,mcs814x-gpio" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver mcs814x_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "mcs814x-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mcs814x_gpio_ids,
|
||||
},
|
||||
.probe = mcs814x_gpio_probe,
|
||||
};
|
||||
|
||||
int __init mcs814x_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&mcs814x_gpio_driver);
|
||||
}
|
||||
postcore_initcall(mcs814x_gpio_init);
|
||||
@@ -0,0 +1,4 @@
|
||||
config NUPORT_ETHERNET_DRIVER
|
||||
tristate "MCS8140 Ethernet driver"
|
||||
depends on ETHERNET && ARCH_MCS814X
|
||||
help
|
||||
@@ -0,0 +1,3 @@
|
||||
obj-$(CONFIG_NUPORT_ETHERNET_DRIVER) += mcs8140.o
|
||||
|
||||
mcs8140-objs := nuport_mac.o
|
||||
File diff suppressed because it is too large
Load Diff
64
target/linux/mcs814x/files-3.3/drivers/net/phy/mcs814x.c
Normal file
64
target/linux/mcs814x/files-3.3/drivers/net/phy/mcs814x.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Driver for Moschip MCS814x internal PHY
|
||||
*
|
||||
* Copyright (c) 2012 Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
MODULE_DESCRIPTION("Moschip MCS814x PHY driver");
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* Nothing special about this PHY but its OUI (O) */
|
||||
static struct phy_driver mcs8140_driver = {
|
||||
.phy_id = 0,
|
||||
.name = "Moschip MCS8140",
|
||||
.phy_id_mask = 0x02,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.config_aneg = &genphy_config_aneg,
|
||||
.read_status = &genphy_read_status,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
||||
.driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init mcs814x_phy_init(void)
|
||||
{
|
||||
return phy_driver_register(&mcs8140_driver);
|
||||
}
|
||||
|
||||
static void __exit mcs814x_phy_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&mcs8140_driver);
|
||||
}
|
||||
|
||||
module_init(mcs814x_phy_init);
|
||||
module_exit(mcs814x_phy_exit);
|
||||
|
||||
static struct mdio_device_id __maybe_unused mcs814x_phy_tbl[] = {
|
||||
{ 0x0, 0x0ffffff0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(mdio, mcs814x_phy_tbl);
|
||||
165
target/linux/mcs814x/files-3.3/drivers/usb/host/ehci-mcs814x.c
Normal file
165
target/linux/mcs814x/files-3.3/drivers/usb/host/ehci-mcs814x.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* MCS814X EHCI Host Controller Driver
|
||||
*
|
||||
* Based on "ehci-fsl.c" by Randy Vinson <rvinson@mvista.com>
|
||||
*
|
||||
* 2007 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define MCS814X_EHCI_CAPS_OFFSET 0x68
|
||||
|
||||
static int mcs814x_ehci_init(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval = 0;
|
||||
|
||||
ehci->caps = hcd->regs + MCS814X_EHCI_CAPS_OFFSET;
|
||||
ehci->regs = hcd->regs
|
||||
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
ehci_reset(ehci);
|
||||
|
||||
retval = ehci_init(hcd);
|
||||
if (retval) {
|
||||
pr_err("ehci_init failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct hc_driver mcs814x_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "MCS814X EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
.reset = mcs814x_ehci_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.get_frame_number = ehci_get_frame,
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
#if defined(CONFIG_PM)
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int mcs814x_ehci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
const struct hc_driver *driver = &mcs814x_ehci_hc_driver;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int retval;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no IRQ. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
irq = res->start;
|
||||
|
||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no register addr. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
retval = -ENODEV;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||
retval = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
retval = -EFAULT;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (retval)
|
||||
goto fail_add_hcd;
|
||||
|
||||
dev_info(&pdev->dev, "added MCS814X EHCI driver\n");
|
||||
|
||||
return retval;
|
||||
|
||||
fail_add_hcd:
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int mcs814x_ehci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:mcs814x-ehci");
|
||||
|
||||
static const struct of_device_id mcs814x_ehci_id[] = {
|
||||
{ .compatible = "moschip,mcs814x-ehci" },
|
||||
{ .compatible = "usb-ehci" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver mcs814x_ehci_driver = {
|
||||
.probe = mcs814x_ehci_probe,
|
||||
.remove = mcs814x_ehci_remove,
|
||||
.driver = {
|
||||
.name = "mcs814x-ehci",
|
||||
.of_match_table = mcs814x_ehci_id,
|
||||
},
|
||||
};
|
||||
202
target/linux/mcs814x/files-3.3/drivers/usb/host/ohci-mcs814x.c
Normal file
202
target/linux/mcs814x/files-3.3/drivers/usb/host/ohci-mcs814x.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* OHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
|
||||
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
|
||||
* (C) Copyright 2002 Hewlett-Packard Company
|
||||
*
|
||||
* Bus Glue for Moschip MCS814x.
|
||||
*
|
||||
* Written by Christopher Hoover <ch@hpl.hp.com>
|
||||
* Based on fragments of previous driver by Russell King et al.
|
||||
*
|
||||
* Modified for LH7A404 from ohci-sa1111.c
|
||||
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
|
||||
*
|
||||
* Modified for pxa27x from ohci-lh7a404.c
|
||||
* by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
|
||||
*
|
||||
* Modified for mcs814x from ohci-mcs814x.c
|
||||
* by Lennert Buytenhek <buytenh@wantstofly.org> 28-2-2006
|
||||
* Based on an earlier driver by Ray Lehtiniemi
|
||||
*
|
||||
* This file is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static int usb_hcd_mcs814x_probe(const struct hc_driver *driver,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int retval;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
|
||||
pr_debug("resource[1] is not IORESOURCE_IRQ");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, "mcs814x");
|
||||
if (hcd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
hcd->rsrc_start = pdev->resource[0].start;
|
||||
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
usb_put_hcd(hcd);
|
||||
retval = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
pr_debug("ioremap failed");
|
||||
retval = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ohci_hcd_init(hcd_to_ohci(hcd));
|
||||
|
||||
retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err1:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void usb_hcd_mcs814x_remove(struct usb_hcd *hcd,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static int __devinit ohci_mcs814x_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int ret;
|
||||
|
||||
ret = ohci_init(ohci);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ohci_run(ohci);
|
||||
if (ret < 0) {
|
||||
err("can't start %s", hcd->self.bus_name);
|
||||
ohci_stop(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hc_driver ohci_mcs814x_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "MCS814X OHCI",
|
||||
.hcd_priv_size = sizeof(struct ohci_hcd),
|
||||
.irq = ohci_irq,
|
||||
.flags = HCD_USB11 | HCD_MEMORY,
|
||||
.start = ohci_mcs814x_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
.urb_enqueue = ohci_urb_enqueue,
|
||||
.urb_dequeue = ohci_urb_dequeue,
|
||||
.endpoint_disable = ohci_endpoint_disable,
|
||||
.get_frame_number = ohci_get_frame,
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
extern int usb_disabled(void);
|
||||
|
||||
static int ohci_hcd_mcs814x_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!usb_disabled())
|
||||
ret = usb_hcd_mcs814x_probe(&ohci_mcs814x_hc_driver, pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ohci_hcd_mcs814x_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_hcd_mcs814x_remove(hcd, pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ohci_hcd_mcs814x_drv_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ohci_hcd_mcs814x_drv_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int status;
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
ohci_finish_controller_resume(hcd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct of_device_id mcs814x_ohci_id[] = {
|
||||
{ .compatible = "moschip,mcs814x-ohci" },
|
||||
{ .compatible = "ohci-le" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver ohci_hcd_mcs814x_driver = {
|
||||
.probe = ohci_hcd_mcs814x_drv_probe,
|
||||
.remove = ohci_hcd_mcs814x_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ohci_hcd_mcs814x_drv_suspend,
|
||||
.resume = ohci_hcd_mcs814x_drv_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "mcs814x-ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mcs814x_ohci_id,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:mcs814x-ohci");
|
||||
207
target/linux/mcs814x/files-3.3/drivers/watchdog/mcs814x_wdt.c
Normal file
207
target/linux/mcs814x/files-3.3/drivers/watchdog/mcs814x_wdt.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Moschip MCS814x Watchdog driver
|
||||
*
|
||||
* Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define WDT_COUNT 0x00
|
||||
#define WDT_CTRL 0x04
|
||||
#define WDT_CTRL_EN 0x1
|
||||
|
||||
/* watchdog frequency */
|
||||
#define WDT_MAX_VALUE (0xffffffff)
|
||||
|
||||
struct mcs814x_wdt {
|
||||
void __iomem *regs;
|
||||
spinlock_t lock;
|
||||
struct watchdog_device wdt_dev;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int mcs814x_wdt_start(struct watchdog_device *dev)
|
||||
{
|
||||
struct mcs814x_wdt *wdt = watchdog_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
spin_lock(&wdt->lock);
|
||||
reg = __raw_readl(wdt->regs + WDT_CTRL);
|
||||
reg |= WDT_CTRL_EN;
|
||||
__raw_writel(reg, wdt->regs + WDT_CTRL);
|
||||
spin_unlock(&wdt->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs814x_wdt_stop(struct watchdog_device *dev)
|
||||
{
|
||||
struct mcs814x_wdt *wdt = watchdog_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
spin_lock(&wdt->lock);
|
||||
reg = __raw_readl(wdt->regs + WDT_CTRL);
|
||||
reg &= ~WDT_CTRL_EN;
|
||||
__raw_writel(reg, wdt->regs + WDT_CTRL);
|
||||
spin_unlock(&wdt->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs814x_wdt_set_timeout(struct watchdog_device *dev,
|
||||
unsigned int new_timeout)
|
||||
{
|
||||
struct mcs814x_wdt *wdt = watchdog_get_drvdata(dev);
|
||||
|
||||
spin_lock(&wdt->lock);
|
||||
/* watchdog counts upward and rollover (0xfffffff -> 0)
|
||||
* triggers the reboot
|
||||
*/
|
||||
__raw_writel(WDT_MAX_VALUE - (new_timeout * clk_get_rate(wdt->clk)),
|
||||
wdt->regs + WDT_COUNT);
|
||||
spin_unlock(&wdt->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs814x_wdt_ping(struct watchdog_device *dev)
|
||||
{
|
||||
/* restart the watchdog */
|
||||
mcs814x_wdt_stop(dev);
|
||||
mcs814x_wdt_set_timeout(dev, dev->timeout);
|
||||
mcs814x_wdt_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info mcs814x_wdt_ident = {
|
||||
.options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT |
|
||||
WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
|
||||
.identity = "MCS814x Watchdog",
|
||||
};
|
||||
|
||||
static struct watchdog_ops mcs814x_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = mcs814x_wdt_start,
|
||||
.stop = mcs814x_wdt_stop,
|
||||
.set_timeout = mcs814x_wdt_set_timeout,
|
||||
.ping = mcs814x_wdt_ping,
|
||||
};
|
||||
|
||||
static int __devinit mcs814x_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct mcs814x_wdt *wdt;
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
clk = clk_get(NULL, "wdt");
|
||||
if (IS_ERR_OR_NULL(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get watchdog clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
|
||||
if (!wdt) {
|
||||
ret = -ENOMEM;
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
spin_lock_init(&wdt->lock);
|
||||
wdt->clk = clk;
|
||||
wdt->wdt_dev.info = &mcs814x_wdt_ident;
|
||||
wdt->wdt_dev.ops = &mcs814x_wdt_ops;
|
||||
wdt->wdt_dev.min_timeout = 1;
|
||||
/* approximately 10995 secs */
|
||||
wdt->wdt_dev.max_timeout = (WDT_MAX_VALUE / clk_get_rate(clk));
|
||||
|
||||
platform_set_drvdata(pdev, wdt);
|
||||
|
||||
/* only ioremap registers, because the register is shared */
|
||||
wdt->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!wdt->regs) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
watchdog_set_drvdata(&wdt->wdt_dev, wdt);
|
||||
|
||||
ret = watchdog_register_device(&wdt->wdt_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot register watchdog: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "registered\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(wdt);
|
||||
out_clk:
|
||||
clk_put(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mcs814x_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mcs814x_wdt *wdt = platform_get_drvdata(pdev);
|
||||
|
||||
clk_put(wdt->clk);
|
||||
watchdog_unregister_device(&wdt->wdt_dev);
|
||||
watchdog_set_drvdata(&wdt->wdt_dev, NULL);
|
||||
kfree(wdt);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mcs814x_wdt_ids[] = {
|
||||
{ .compatible = "moschip,mcs814x-wdt", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver mcs814x_wdt_driver = {
|
||||
.driver = {
|
||||
.name = "mcs814x-wdt",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mcs814x_wdt_ids,
|
||||
},
|
||||
.probe = mcs814x_wdt_probe,
|
||||
.remove = __devexit_p(mcs814x_wdt_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(mcs814x_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_DESCRIPTION("Moschip MCS814x Watchdog driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
MODULE_ALIAS("platform:mcs814x-wdt");
|
||||
Reference in New Issue
Block a user