diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/of_mmc_spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 8 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/mpc52xx_psc_spi.c | 25 | ||||
-rw-r--r-- | drivers/spi/mpc52xx_spi.c | 520 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.c | 3 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 4 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/mpc5200_wdt.c | 293 |
9 files changed, 553 insertions, 304 deletions
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index 0c44d56..0c7a63c 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -22,6 +22,8 @@ #include <linux/mmc/core.h> #include <linux/mmc/host.h> +MODULE_LICENSE("GPL"); + enum { CD_GPIO = 0, WP_GPIO, diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 94058c6..28fce65 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -133,6 +133,14 @@ config SPI_LM70_LLP which interfaces to an LM70 temperature sensor using a parallel port. +config SPI_MPC52xx + tristate "Freescale MPC52xx SPI (non-PSC) controller support" + depends on PPC_MPC52xx && SPI + select SPI_MASTER_OF + help + This drivers supports the MPC52xx SPI controller in master SPI + mode. + config SPI_MPC52xx_PSC tristate "Freescale MPC52xx PSC SPI controller" depends on PPC_MPC52xx && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 21a1182..e3f092a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o +obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 1b74d5c..f50c81d 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -17,6 +17,7 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/of_platform.h> +#include <linux/of_spi.h> #include <linux/workqueue.h> #include <linux/completion.h> #include <linux/io.h> @@ -313,11 +314,13 @@ static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) struct mpc52xx_psc __iomem *psc = mps->psc; struct mpc52xx_psc_fifo __iomem *fifo = mps->fifo; u32 mclken_div; - int ret = 0; + int ret; /* default sysclk is 512MHz */ mclken_div = (mps->sysclk ? mps->sysclk : 512000000) / MCLK; - mpc52xx_set_psc_clkdiv(psc_id, mclken_div); + ret = mpc52xx_set_psc_clkdiv(psc_id, mclken_div); + if (ret) + return ret; /* Reset the PSC into a known state */ out_8(&psc->command, MPC52xx_PSC_RST_RX); @@ -341,7 +344,7 @@ static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) mps->bits_per_word = 8; - return ret; + return 0; } static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) @@ -410,8 +413,10 @@ static int __init mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, goto free_master; ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "can't configure PSC! Is it capable of SPI?\n"); goto free_irq; + } spin_lock_init(&mps->lock); init_completion(&mps->done); @@ -464,10 +469,11 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, const u32 *regaddr_p; u64 regaddr64, size64; s16 id = -1; + int rc; regaddr_p = of_get_address(op->node, 0, &size64, NULL); if (!regaddr_p) { - printk(KERN_ERR "Invalid PSC address\n"); + dev_err(&op->dev, "Invalid PSC address\n"); return -EINVAL; } regaddr64 = of_translate_address(op->node, regaddr_p); @@ -478,15 +484,18 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, psc_nump = of_get_property(op->node, "cell-index", NULL); if (!psc_nump || *psc_nump > 5) { - printk(KERN_ERR "mpc52xx_psc_spi: Device node %s has invalid " - "cell-index property\n", op->node->full_name); + dev_err(&op->dev, "Invalid cell-index property\n"); return -EINVAL; } id = *psc_nump + 1; } - return mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, + rc = mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, irq_of_parse_and_map(op->node, 0), id); + if (rc == 0) + of_register_spi_devices(dev_get_drvdata(&op->dev), op->node); + + return rc; } static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c new file mode 100644 index 0000000..ef8379b --- /dev/null +++ b/drivers/spi/mpc52xx_spi.c @@ -0,0 +1,520 @@ +/* + * MPC52xx SPI bus driver. + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * + * This file is released under the GPLv2 + * + * This is the driver for the MPC5200's dedicated SPI controller. + * + * Note: this driver does not support the MPC5200 PSC in SPI mode. For + * that driver see drivers/spi/mpc52xx_psc_spi.c + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/spi/mpc52xx_spi.h> +#include <linux/of_spi.h> +#include <linux/io.h> +#include <asm/time.h> +#include <asm/mpc52xx.h> + +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +MODULE_DESCRIPTION("MPC52xx SPI (non-PSC) Driver"); +MODULE_LICENSE("GPL"); + +/* Register offsets */ +#define SPI_CTRL1 0x00 +#define SPI_CTRL1_SPIE (1 << 7) +#define SPI_CTRL1_SPE (1 << 6) +#define SPI_CTRL1_MSTR (1 << 4) +#define SPI_CTRL1_CPOL (1 << 3) +#define SPI_CTRL1_CPHA (1 << 2) +#define SPI_CTRL1_SSOE (1 << 1) +#define SPI_CTRL1_LSBFE (1 << 0) + +#define SPI_CTRL2 0x01 +#define SPI_BRR 0x04 + +#define SPI_STATUS 0x05 +#define SPI_STATUS_SPIF (1 << 7) +#define SPI_STATUS_WCOL (1 << 6) +#define SPI_STATUS_MODF (1 << 4) + +#define SPI_DATA 0x09 +#define SPI_PORTDATA 0x0d +#define SPI_DATADIR 0x10 + +/* FSM state return values */ +#define FSM_STOP 0 /* Nothing more for the state machine to */ + /* do. If something interesting happens */ + /* then and IRQ will be received */ +#define FSM_POLL 1 /* need to poll for completion, an IRQ is */ + /* not expected */ +#define FSM_CONTINUE 2 /* Keep iterating the state machine */ + +/* Driver internal data */ +struct mpc52xx_spi { + struct spi_master *master; + u32 sysclk; + void __iomem *regs; + int irq0; /* MODF irq */ + int irq1; /* SPIF irq */ + int ipb_freq; + + /* Statistics */ + int msg_count; + int wcol_count; + int wcol_ticks; + u32 wcol_tx_timestamp; + int modf_count; + int byte_count; + + struct list_head queue; /* queue of pending messages */ + spinlock_t lock; + struct work_struct work; + + + /* Details of current transfer (length, and buffer pointers) */ + struct spi_message *message; /* current message */ + struct spi_transfer *transfer; /* current transfer */ + int (*state)(int irq, struct mpc52xx_spi *ms, u8 status, u8 data); + int len; + int timestamp; + u8 *rx_buf; + const u8 *tx_buf; + int cs_change; +}; + +/* + * CS control function + */ +static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value) +{ + out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); +} + +/* + * Start a new transfer. This is called both by the idle state + * for the first transfer in a message, and by the wait state when the + * previous transfer in a message is complete. + */ +static void mpc52xx_spi_start_transfer(struct mpc52xx_spi *ms) +{ + ms->rx_buf = ms->transfer->rx_buf; + ms->tx_buf = ms->transfer->tx_buf; + ms->len = ms->transfer->len; + + /* Activate the chip select */ + if (ms->cs_change) + mpc52xx_spi_chipsel(ms, 1); + ms->cs_change = ms->transfer->cs_change; + + /* Write out the first byte */ + ms->wcol_tx_timestamp = get_tbl(); + if (ms->tx_buf) + out_8(ms->regs + SPI_DATA, *ms->tx_buf++); + else + out_8(ms->regs + SPI_DATA, 0); +} + +/* Forward declaration of state handlers */ +static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, + u8 status, u8 data); +static int mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, + u8 status, u8 data); + +/* + * IDLE state + * + * No transfers are in progress; if another transfer is pending then retrieve + * it and kick it off. Otherwise, stop processing the state machine + */ +static int +mpc52xx_spi_fsmstate_idle(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) +{ + struct spi_device *spi; + int spr, sppr; + u8 ctrl1; + + if (status && (irq != NO_IRQ)) + dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n", + status); + + /* Check if there is another transfer waiting. */ + if (list_empty(&ms->queue)) + return FSM_STOP; + + /* get the head of the queue */ + ms->message = list_first_entry(&ms->queue, struct spi_message, queue); + list_del_init(&ms->message->queue); + + /* Setup the controller parameters */ + ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR; + spi = ms->message->spi; + if (spi->mode & SPI_CPHA) + ctrl1 |= SPI_CTRL1_CPHA; + if (spi->mode & SPI_CPOL) + ctrl1 |= SPI_CTRL1_CPOL; + if (spi->mode & SPI_LSB_FIRST) + ctrl1 |= SPI_CTRL1_LSBFE; + out_8(ms->regs + SPI_CTRL1, ctrl1); + + /* Setup the controller speed */ + /* minimum divider is '2'. Also, add '1' to force rounding the + * divider up. */ + sppr = ((ms->ipb_freq / ms->message->spi->max_speed_hz) + 1) >> 1; + spr = 0; + if (sppr < 1) + sppr = 1; + while (((sppr - 1) & ~0x7) != 0) { + sppr = (sppr + 1) >> 1; /* add '1' to force rounding up */ + spr++; + } + sppr--; /* sppr quantity in register is offset by 1 */ + if (spr > 7) { + /* Don't overrun limits of SPI baudrate register */ + spr = 7; + sppr = 7; + } + out_8(ms->regs + SPI_BRR, sppr << 4 | spr); /* Set speed */ + + ms->cs_change = 1; + ms->transfer = container_of(ms->message->transfers.next, + struct spi_transfer, transfer_list); + + mpc52xx_spi_start_transfer(ms); + ms->state = mpc52xx_spi_fsmstate_transfer; + + return FSM_CONTINUE; +} + +/* + * TRANSFER state + * + * In the middle of a transfer. If the SPI core has completed processing + * a byte, then read out the received data and write out the next byte + * (unless this transfer is finished; in which case go on to the wait + * state) + */ +static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, + u8 status, u8 data) +{ + if (!status) + return ms->irq0 ? FSM_STOP : FSM_POLL; + + if (status & SPI_STATUS_WCOL) { + /* The SPI controller is stoopid. At slower speeds, it may + * raise the SPIF flag before the state machine is actually + * finished, which causes a collision (internal to the state + * machine only). The manual recommends inserting a delay + * between receiving the interrupt and sending the next byte, + * but it can also be worked around simply by retrying the + * transfer which is what we do here. */ + ms->wcol_count++; + ms->wcol_ticks += get_tbl() - ms->wcol_tx_timestamp; + ms->wcol_tx_timestamp = get_tbl(); + data = 0; + if (ms->tx_buf) + data = *(ms->tx_buf-1); + out_8(ms->regs + SPI_DATA, data); /* try again */ + return FSM_CONTINUE; + } else if (status & SPI_STATUS_MODF) { + ms->modf_count++; + dev_err(&ms->master->dev, "mode fault\n"); + mpc52xx_spi_chipsel(ms, 0); + ms->message->status = -EIO; + ms->message->complete(ms->message->context); + ms->state = mpc52xx_spi_fsmstate_idle; + return FSM_CONTINUE; + } + + /* Read data out of the spi device */ + ms->byte_count++; + if (ms->rx_buf) + *ms->rx_buf++ = data; + + /* Is the transfer complete? */ + ms->len--; + if (ms->len == 0) { + ms->timestamp = get_tbl(); + ms->timestamp += ms->transfer->delay_usecs * tb_ticks_per_usec; + ms->state = mpc52xx_spi_fsmstate_wait; + return FSM_CONTINUE; + } + + /* Write out the next byte */ + ms->wcol_tx_timestamp = get_tbl(); + if (ms->tx_buf) + out_8(ms->regs + SPI_DATA, *ms->tx_buf++); + else + out_8(ms->regs + SPI_DATA, 0); + + return FSM_CONTINUE; +} + +/* + * WAIT state + * + * A transfer has completed; need to wait for the delay period to complete + * before starting the next transfer + */ +static int +mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) +{ + if (status && irq) + dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n", + status); + + if (((int)get_tbl()) - ms->timestamp < 0) + return FSM_POLL; + + ms->message->actual_length += ms->transfer->len; + + /* Check if there is another transfer in this message. If there + * aren't then deactivate CS, notify sender, and drop back to idle + * to start the next message. */ + if (ms->transfer->transfer_list.next == &ms->message->transfers) { + ms->msg_count++; + mpc52xx_spi_chipsel(ms, 0); + ms->message->status = 0; + ms->message->complete(ms->message->context); + ms->state = mpc52xx_spi_fsmstate_idle; + return FSM_CONTINUE; + } + + /* There is another transfer; kick it off */ + + if (ms->cs_change) + mpc52xx_spi_chipsel(ms, 0); + + ms->transfer = container_of(ms->transfer->transfer_list.next, + struct spi_transfer, transfer_list); + mpc52xx_spi_start_transfer(ms); + ms->state = mpc52xx_spi_fsmstate_transfer; + return FSM_CONTINUE; +} + +/** + * mpc52xx_spi_fsm_process - Finite State Machine iteration function + * @irq: irq number that triggered the FSM or 0 for polling + * @ms: pointer to mpc52xx_spi driver data + */ +static void mpc52xx_spi_fsm_process(int irq, struct mpc52xx_spi *ms) +{ + int rc = FSM_CONTINUE; + u8 status, data; + + while (rc == FSM_CONTINUE) { + /* Interrupt cleared by read of STATUS followed by + * read of DATA registers */ + status = in_8(ms->regs + SPI_STATUS); + data = in_8(ms->regs + SPI_DATA); + rc = ms->state(irq, ms, status, data); + } + + if (rc == FSM_POLL) + schedule_work(&ms->work); +} + +/** + * mpc52xx_spi_irq - IRQ handler + */ +static irqreturn_t mpc52xx_spi_irq(int irq, void *_ms) +{ + struct mpc52xx_spi *ms = _ms; + spin_lock(&ms->lock); + mpc52xx_spi_fsm_process(irq, ms); + spin_unlock(&ms->lock); + return IRQ_HANDLED; +} + +/** + * mpc52xx_spi_wq - Workqueue function for polling the state machine + */ +static void mpc52xx_spi_wq(struct work_struct *work) +{ + struct mpc52xx_spi *ms = container_of(work, struct mpc52xx_spi, work); + unsigned long flags; + + spin_lock_irqsave(&ms->lock, flags); + mpc52xx_spi_fsm_process(0, ms); + spin_unlock_irqrestore(&ms->lock, flags); +} + +/* + * spi_master ops + */ + +static int mpc52xx_spi_setup(struct spi_device *spi) +{ + if (spi->bits_per_word % 8) + return -EINVAL; + + if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) + return -EINVAL; + + if (spi->chip_select >= spi->master->num_chipselect) + return -EINVAL; + + return 0; +} + +static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&ms->lock, flags); + list_add_tail(&m->queue, &ms->queue); + spin_unlock_irqrestore(&ms->lock, flags); + schedule_work(&ms->work); + + return 0; +} + +/* + * OF Platform Bus Binding + */ +static int __devinit mpc52xx_spi_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct spi_master *master; + struct mpc52xx_spi *ms; + void __iomem *regs; + int rc; + + /* MMIO registers */ + dev_dbg(&op->dev, "probing mpc5200 SPI device\n"); + regs = of_iomap(op->node, 0); + if (!regs) + return -ENODEV; + + /* initialize the device */ + out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR); + out_8(regs + SPI_CTRL2, 0x0); + out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */ + out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */ + + /* Clear the status register and re-read it to check for a MODF + * failure. This driver cannot currently handle multiple masters + * on the SPI bus. This fault will also occur if the SPI signals + * are not connected to any pins (port_config setting) */ + in_8(regs + SPI_STATUS); + in_8(regs + SPI_DATA); + if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) { + dev_err(&op->dev, "mode fault; is port_config correct?\n"); + rc = -EIO; + goto err_init; + } + + dev_dbg(&op->dev, "allocating spi_master struct\n"); + master = spi_alloc_master(&op->dev, sizeof *ms); + if (!master) { + rc = -ENOMEM; + goto err_alloc; + } + master->bus_num = -1; + master->num_chipselect = 1; + master->setup = mpc52xx_spi_setup; + master->transfer = mpc52xx_spi_transfer; + dev_set_drvdata(&op->dev, master); + + ms = spi_master_get_devdata(master); + ms->master = master; + ms->regs = regs; + ms->irq0 = irq_of_parse_and_map(op->node, 0); + ms->irq1 = irq_of_parse_and_map(op->node, 1); + ms->state = mpc52xx_spi_fsmstate_idle; + ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node); + spin_lock_init(&ms->lock); + INIT_LIST_HEAD(&ms->queue); + INIT_WORK(&ms->work, mpc52xx_spi_wq); + + /* Decide if interrupts can be used */ + if (ms->irq0 && ms->irq1) { + rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, + "mpc5200-spi-modf", ms); + rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, + "mpc5200-spi-spiF", ms); + if (rc) { + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + ms->irq0 = ms->irq1 = 0; + } + } else { + /* operate in polled mode */ + ms->irq0 = ms->irq1 = 0; + } + + if (!ms->irq0) + dev_info(&op->dev, "using polled mode\n"); + + dev_dbg(&op->dev, "registering spi_master struct\n"); + rc = spi_register_master(master); + if (rc) + goto err_register; + + of_register_spi_devices(master, op->node); + dev_info(&ms->master->dev, "registered MPC5200 SPI bus\n"); + + return rc; + + err_register: + dev_err(&ms->master->dev, "initialization failed\n"); + spi_master_put(master); + err_alloc: + err_init: + iounmap(regs); + return rc; +} + +static int __devexit mpc52xx_spi_remove(struct of_device *op) +{ + struct spi_master *master = dev_get_drvdata(&op->dev); + struct mpc52xx_spi *ms = spi_master_get_devdata(master); + + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + + spi_unregister_master(master); + spi_master_put(master); + iounmap(ms->regs); + + return 0; +} + +static struct of_device_id mpc52xx_spi_match[] __devinitdata = { + { .compatible = "fsl,mpc5200-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, mpc52xx_spi_match); + +static struct of_platform_driver mpc52xx_spi_of_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx-spi", + .match_table = mpc52xx_spi_match, + .probe = mpc52xx_spi_probe, + .remove = __exit_p(mpc52xx_spi_remove), +}; + +static int __init mpc52xx_spi_init(void) +{ + return of_register_platform_driver(&mpc52xx_spi_of_driver); +} +module_init(mpc52xx_spi_init); + +static void __exit mpc52xx_spi_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_spi_of_driver); +} +module_exit(mpc52xx_spi_exit); + diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 46b8c5c..5a143b9 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -148,7 +148,8 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, { u8 bits_per_word; - bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; + bits_per_word = (t && t->bits_per_word) + ? t->bits_per_word : spi->bits_per_word; if (bits_per_word != 8) { dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", __func__, bits_per_word); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3711b88..d958b76 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -861,8 +861,10 @@ config GEF_WDT Watchdog timer found in a number of GE Fanuc single board computers. config MPC5200_WDT - tristate "MPC5200 Watchdog Timer" + bool "MPC52xx Watchdog Timer" depends on PPC_MPC52xx + help + Use General Purpose Timer (GPT) 0 on the MPC5200 as Watchdog. config 8xxx_WDT tristate "MPC8xxx Platform Watchdog Timer" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 699199b..89c045d 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -118,7 +118,6 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o # POWERPC Architecture obj-$(CONFIG_GEF_WDT) += gef_wdt.o -obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o obj-$(CONFIG_PIKA_WDT) += pika_wdt.o diff --git a/drivers/watchdog/mpc5200_wdt.c b/drivers/watchdog/mpc5200_wdt.c deleted file mode 100644 index fa9c47c..0000000 --- a/drivers/watchdog/mpc5200_wdt.c +++ /dev/null @@ -1,293 +0,0 @@ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/io.h> -#include <linux/spinlock.h> -#include <linux/of_platform.h> -#include <linux/uaccess.h> -#include <asm/mpc52xx.h> - - -#define GPT_MODE_WDT (1 << 15) -#define GPT_MODE_CE (1 << 12) -#define GPT_MODE_MS_TIMER (0x4) - - -struct mpc5200_wdt { - unsigned count; /* timer ticks before watchdog kicks in */ - long ipb_freq; - struct miscdevice miscdev; - struct resource mem; - struct mpc52xx_gpt __iomem *regs; - spinlock_t io_lock; -}; - -/* is_active stores wether or not the /dev/watchdog device is opened */ -static unsigned long is_active; - -/* misc devices don't provide a way, to get back to 'dev' or 'miscdev' from - * file operations, which sucks. But there can be max 1 watchdog anyway, so... - */ -static struct mpc5200_wdt *wdt_global; - - -/* helper to calculate timeout in timer counts */ -static void mpc5200_wdt_set_timeout(struct mpc5200_wdt *wdt, int timeout) -{ - /* use biggest prescaler of 64k */ - wdt->count = (wdt->ipb_freq + 0xffff) / 0x10000 * timeout; - - if (wdt->count > 0xffff) - wdt->count = 0xffff; -} -/* return timeout in seconds (calculated from timer count) */ -static int mpc5200_wdt_get_timeout(struct mpc5200_wdt *wdt) -{ - return wdt->count * 0x10000 / wdt->ipb_freq; -} - - -/* watchdog operations */ -static int mpc5200_wdt_start(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* disable */ - out_be32(&wdt->regs->mode, 0); - /* set timeout, with maximum prescaler */ - out_be32(&wdt->regs->count, 0x0 | wdt->count); - /* enable watchdog */ - out_be32(&wdt->regs->mode, GPT_MODE_CE | GPT_MODE_WDT | - GPT_MODE_MS_TIMER); - spin_unlock(&wdt->io_lock); - - return 0; -} -static int mpc5200_wdt_ping(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* writing A5 to OCPW resets the watchdog */ - out_be32(&wdt->regs->mode, 0xA5000000 | - (0xffffff & in_be32(&wdt->regs->mode))); - spin_unlock(&wdt->io_lock); - return 0; -} -static int mpc5200_wdt_stop(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* disable */ - out_be32(&wdt->regs->mode, 0); - spin_unlock(&wdt->io_lock); - return 0; -} - - -/* file operations */ -static ssize_t mpc5200_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - struct mpc5200_wdt *wdt = file->private_data; - mpc5200_wdt_ping(wdt); - return 0; -} -static struct watchdog_info mpc5200_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "mpc5200 watchdog on GPT0", -}; -static long mpc5200_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct mpc5200_wdt *wdt = file->private_data; - int __user *data = (int __user *)arg; - int timeout; - int ret = 0; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user(data, &mpc5200_wdt_info, - sizeof(mpc5200_wdt_info)); - if (ret) - ret = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, data); - break; - - case WDIOC_KEEPALIVE: - mpc5200_wdt_ping(wdt); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(timeout, data); - if (ret) - break; - mpc5200_wdt_set_timeout(wdt, timeout); - mpc5200_wdt_start(wdt); - /* fall through and return the timeout */ - - case WDIOC_GETTIMEOUT: - timeout = mpc5200_wdt_get_timeout(wdt); - ret = put_user(timeout, data); - break; - - default: - ret = -ENOTTY; - } - return ret; -} - -static int mpc5200_wdt_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &is_active)) - return -EBUSY; - - /* Set and activate the watchdog */ - mpc5200_wdt_set_timeout(wdt_global, 30); - mpc5200_wdt_start(wdt_global); - file->private_data = wdt_global; - return nonseekable_open(inode, file); -} -static int mpc5200_wdt_release(struct inode *inode, struct file *file) -{ -#if WATCHDOG_NOWAYOUT == 0 - struct mpc5200_wdt *wdt = file->private_data; - mpc5200_wdt_stop(wdt); - wdt->count = 0; /* == disabled */ -#endif - clear_bit(0, &is_active); - return 0; -} - -static const struct file_operations mpc5200_wdt_fops = { - .owner = THIS_MODULE, - .write = mpc5200_wdt_write, - .unlocked_ioctl = mpc5200_wdt_ioctl, - .open = mpc5200_wdt_open, - .release = mpc5200_wdt_release, -}; - -/* module operations */ -static int mpc5200_wdt_probe(struct of_device *op, - const struct of_device_id *match) -{ - struct mpc5200_wdt *wdt; - int err; - const void *has_wdt; - int size; - - has_wdt = of_get_property(op->node, "has-wdt", NULL); - if (!has_wdt) - has_wdt = of_get_property(op->node, "fsl,has-wdt", NULL); - if (!has_wdt) - return -ENODEV; - - wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); - if (!wdt) - return -ENOMEM; - - wdt->ipb_freq = mpc5xxx_get_bus_frequency(op->node); - - err = of_address_to_resource(op->node, 0, &wdt->mem); - if (err) - goto out_free; - size = wdt->mem.end - wdt->mem.start + 1; - if (!request_mem_region(wdt->mem.start, size, "mpc5200_wdt")) { - err = -ENODEV; - goto out_free; - } - wdt->regs = ioremap(wdt->mem.start, size); - if (!wdt->regs) { - err = -ENODEV; - goto out_release; - } - - dev_set_drvdata(&op->dev, wdt); - spin_lock_init(&wdt->io_lock); - - wdt->miscdev = (struct miscdevice) { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc5200_wdt_fops, - .parent = &op->dev, - }; - wdt_global = wdt; - err = misc_register(&wdt->miscdev); - if (!err) - return 0; - - iounmap(wdt->regs); -out_release: - release_mem_region(wdt->mem.start, size); -out_free: - kfree(wdt); - return err; -} - -static int mpc5200_wdt_remove(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - - mpc5200_wdt_stop(wdt); - misc_deregister(&wdt->miscdev); - iounmap(wdt->regs); - release_mem_region(wdt->mem.start, wdt->mem.end - wdt->mem.start + 1); - kfree(wdt); - - return 0; -} -static int mpc5200_wdt_suspend(struct of_device *op, pm_message_t state) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - mpc5200_wdt_stop(wdt); - return 0; -} -static int mpc5200_wdt_resume(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - if (wdt->count) - mpc5200_wdt_start(wdt); - return 0; -} -static int mpc5200_wdt_shutdown(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - mpc5200_wdt_stop(wdt); - return 0; -} - -static struct of_device_id mpc5200_wdt_match[] = { - { .compatible = "mpc5200-gpt", }, - { .compatible = "fsl,mpc5200-gpt", }, - {}, -}; -static struct of_platform_driver mpc5200_wdt_driver = { - .owner = THIS_MODULE, - .name = "mpc5200-gpt-wdt", - .match_table = mpc5200_wdt_match, - .probe = mpc5200_wdt_probe, - .remove = mpc5200_wdt_remove, - .suspend = mpc5200_wdt_suspend, - .resume = mpc5200_wdt_resume, - .shutdown = mpc5200_wdt_shutdown, -}; - - -static int __init mpc5200_wdt_init(void) -{ - return of_register_platform_driver(&mpc5200_wdt_driver); -} - -static void __exit mpc5200_wdt_exit(void) -{ - of_unregister_platform_driver(&mpc5200_wdt_driver); -} - -module_init(mpc5200_wdt_init); -module_exit(mpc5200_wdt_exit); - -MODULE_AUTHOR("Domen Puncer <domen.puncer@telargo.com>"); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |