diff options
| author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-12-09 01:10:22 -0500 |
|---|---|---|
| committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-12-09 01:10:22 -0500 |
| commit | 8c82da5e2495589b7b70a43789e7cbd61a486ed0 (patch) | |
| tree | fdde7380d2584d0297898373831f26cd5208cabd | |
| parent | 51badebdcf394cc5fd574a524b55b3f6085e5e9c (diff) | |
| parent | 6d5355998a757a16815d58da4a739b15a6efbfba (diff) | |
Merge commit 'gcl/next' into next
| -rw-r--r-- | Documentation/powerpc/dts-bindings/fsl/mpc5200.txt | 17 | ||||
| -rw-r--r-- | arch/powerpc/include/asm/mpc52xx.h | 47 | ||||
| -rw-r--r-- | arch/powerpc/kernel/io.c | 4 | ||||
| -rw-r--r-- | arch/powerpc/platforms/52xx/Kconfig | 5 | ||||
| -rw-r--r-- | arch/powerpc/platforms/52xx/Makefile | 1 | ||||
| -rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_gpt.c | 428 | ||||
| -rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 560 | ||||
| -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 | ||||
| -rw-r--r-- | include/linux/spi/mpc52xx_spi.h | 10 |
17 files changed, 1609 insertions, 320 deletions
diff --git a/Documentation/powerpc/dts-bindings/fsl/mpc5200.txt b/Documentation/powerpc/dts-bindings/fsl/mpc5200.txt index 8447fd7090d0..ddd5ee32ea63 100644 --- a/Documentation/powerpc/dts-bindings/fsl/mpc5200.txt +++ b/Documentation/powerpc/dts-bindings/fsl/mpc5200.txt | |||
| @@ -103,7 +103,22 @@ fsl,mpc5200-gpt nodes | |||
| 103 | --------------------- | 103 | --------------------- |
| 104 | On the mpc5200 and 5200b, GPT0 has a watchdog timer function. If the board | 104 | On the mpc5200 and 5200b, GPT0 has a watchdog timer function. If the board |
| 105 | design supports the internal wdt, then the device node for GPT0 should | 105 | design supports the internal wdt, then the device node for GPT0 should |
| 106 | include the empty property 'fsl,has-wdt'. | 106 | include the empty property 'fsl,has-wdt'. Note that this does not activate |
| 107 | the watchdog. The timer will function as a GPT if the timer api is used, and | ||
| 108 | it will function as watchdog if the watchdog device is used. The watchdog | ||
| 109 | mode has priority over the gpt mode, i.e. if the watchdog is activated, any | ||
| 110 | gpt api call to this timer will fail with -EBUSY. | ||
| 111 | |||
| 112 | If you add the property | ||
| 113 | fsl,wdt-on-boot = <n>; | ||
| 114 | GPT0 will be marked as in-use watchdog, i.e. blocking every gpt access to it. | ||
| 115 | If n>0, the watchdog is started with a timeout of n seconds. If n=0, the | ||
| 116 | configuration of the watchdog is not touched. This is useful in two cases: | ||
| 117 | - just mark GPT0 as watchdog, blocking gpt accesses, and configure it later; | ||
| 118 | - do not touch a configuration assigned by the boot loader which supervises | ||
| 119 | the boot process itself. | ||
| 120 | |||
| 121 | The watchdog will respect the CONFIG_WATCHDOG_NOWAYOUT option. | ||
| 107 | 122 | ||
| 108 | An mpc5200-gpt can be used as a single line GPIO controller. To do so, | 123 | An mpc5200-gpt can be used as a single line GPIO controller. To do so, |
| 109 | add the following properties to the gpt node: | 124 | add the following properties to the gpt node: |
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h index 1b4f697abbdd..b664ce79a172 100644 --- a/arch/powerpc/include/asm/mpc52xx.h +++ b/arch/powerpc/include/asm/mpc52xx.h | |||
| @@ -276,6 +276,53 @@ extern int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv); | |||
| 276 | extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node); | 276 | extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node); |
| 277 | extern void mpc52xx_restart(char *cmd); | 277 | extern void mpc52xx_restart(char *cmd); |
| 278 | 278 | ||
| 279 | /* mpc52xx_gpt.c */ | ||
| 280 | struct mpc52xx_gpt_priv; | ||
| 281 | extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq); | ||
| 282 | extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period, | ||
| 283 | int continuous); | ||
| 284 | extern u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt); | ||
| 285 | extern int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt); | ||
| 286 | |||
| 287 | /* mpc52xx_lpbfifo.c */ | ||
| 288 | #define MPC52XX_LPBFIFO_FLAG_READ (0) | ||
| 289 | #define MPC52XX_LPBFIFO_FLAG_WRITE (1<<0) | ||
| 290 | #define MPC52XX_LPBFIFO_FLAG_NO_INCREMENT (1<<1) | ||
| 291 | #define MPC52XX_LPBFIFO_FLAG_NO_DMA (1<<2) | ||
| 292 | #define MPC52XX_LPBFIFO_FLAG_POLL_DMA (1<<3) | ||
| 293 | |||
| 294 | struct mpc52xx_lpbfifo_request { | ||
| 295 | struct list_head list; | ||
| 296 | |||
| 297 | /* localplus bus address */ | ||
| 298 | unsigned int cs; | ||
| 299 | size_t offset; | ||
| 300 | |||
| 301 | /* Memory address */ | ||
| 302 | void *data; | ||
| 303 | phys_addr_t data_phys; | ||
| 304 | |||
| 305 | /* Details of transfer */ | ||
| 306 | size_t size; | ||
| 307 | size_t pos; /* current position of transfer */ | ||
| 308 | int flags; | ||
| 309 | |||
| 310 | /* What to do when finished */ | ||
| 311 | void (*callback)(struct mpc52xx_lpbfifo_request *); | ||
| 312 | |||
| 313 | void *priv; /* Driver private data */ | ||
| 314 | |||
| 315 | /* statistics */ | ||
| 316 | int irq_count; | ||
| 317 | int irq_ticks; | ||
| 318 | u8 last_byte; | ||
| 319 | int buffer_not_done_cnt; | ||
| 320 | }; | ||
| 321 | |||
| 322 | extern int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req); | ||
| 323 | extern void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req); | ||
| 324 | extern void mpc52xx_lpbfifo_poll(void); | ||
| 325 | |||
| 279 | /* mpc52xx_pic.c */ | 326 | /* mpc52xx_pic.c */ |
| 280 | extern void mpc52xx_init_irq(void); | 327 | extern void mpc52xx_init_irq(void); |
| 281 | extern unsigned int mpc52xx_get_irq(void); | 328 | extern unsigned int mpc52xx_get_irq(void); |
diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c index 1882bf419fa6..8dc7547c2377 100644 --- a/arch/powerpc/kernel/io.c +++ b/arch/powerpc/kernel/io.c | |||
| @@ -161,7 +161,7 @@ void _memcpy_fromio(void *dest, const volatile void __iomem *src, | |||
| 161 | dest++; | 161 | dest++; |
| 162 | n--; | 162 | n--; |
| 163 | } | 163 | } |
| 164 | while(n > 4) { | 164 | while(n >= 4) { |
| 165 | *((u32 *)dest) = *((volatile u32 *)vsrc); | 165 | *((u32 *)dest) = *((volatile u32 *)vsrc); |
| 166 | eieio(); | 166 | eieio(); |
| 167 | vsrc += 4; | 167 | vsrc += 4; |
| @@ -190,7 +190,7 @@ void _memcpy_toio(volatile void __iomem *dest, const void *src, unsigned long n) | |||
| 190 | vdest++; | 190 | vdest++; |
| 191 | n--; | 191 | n--; |
| 192 | } | 192 | } |
| 193 | while(n > 4) { | 193 | while(n >= 4) { |
| 194 | *((volatile u32 *)vdest) = *((volatile u32 *)src); | 194 | *((volatile u32 *)vdest) = *((volatile u32 *)src); |
| 195 | src += 4; | 195 | src += 4; |
| 196 | vdest += 4; | 196 | vdest += 4; |
diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig index 8b8e9560a315..47ea1be1481b 100644 --- a/arch/powerpc/platforms/52xx/Kconfig +++ b/arch/powerpc/platforms/52xx/Kconfig | |||
| @@ -62,3 +62,8 @@ config PPC_MPC5200_GPIO | |||
| 62 | select GENERIC_GPIO | 62 | select GENERIC_GPIO |
| 63 | help | 63 | help |
| 64 | Enable gpiolib support for mpc5200 based boards | 64 | Enable gpiolib support for mpc5200 based boards |
| 65 | |||
| 66 | config PPC_MPC5200_LPBFIFO | ||
| 67 | tristate "MPC5200 LocalPlus bus FIFO driver" | ||
| 68 | depends on PPC_MPC52xx | ||
| 69 | select PPC_BESTCOMM_GEN_BD | ||
diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index bfd4f52cf3dd..2bc8cd0c5cfc 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile | |||
| @@ -15,3 +15,4 @@ ifeq ($(CONFIG_PPC_LITE5200),y) | |||
| 15 | endif | 15 | endif |
| 16 | 16 | ||
| 17 | obj-$(CONFIG_PPC_MPC5200_GPIO) += mpc52xx_gpio.o | 17 | obj-$(CONFIG_PPC_MPC5200_GPIO) += mpc52xx_gpio.o |
| 18 | obj-$(CONFIG_PPC_MPC5200_LPBFIFO) += mpc52xx_lpbfifo.o | ||
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c index 4d76b7f2336c..17ecdf4c87ae 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c | |||
| @@ -16,8 +16,14 @@ | |||
| 16 | * output signals or measure input signals. | 16 | * output signals or measure input signals. |
| 17 | * | 17 | * |
| 18 | * This driver supports the GPIO and IRQ controller functions of the GPT | 18 | * This driver supports the GPIO and IRQ controller functions of the GPT |
| 19 | * device. Timer functions are not yet supported, nor is the watchdog | 19 | * device. Timer functions are not yet supported. |
| 20 | * timer. | 20 | * |
| 21 | * The timer gpt0 can be used as watchdog (wdt). If the wdt mode is used, | ||
| 22 | * this prevents the use of any gpt0 gpt function (i.e. they will fail with | ||
| 23 | * -EBUSY). Thus, the safety wdt function always has precedence over the gpt | ||
| 24 | * function. If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT, | ||
| 25 | * this means that gpt0 is locked in wdt mode until the next reboot - this | ||
| 26 | * may be a requirement in safety applications. | ||
| 21 | * | 27 | * |
| 22 | * To use the GPIO function, the following two properties must be added | 28 | * To use the GPIO function, the following two properties must be added |
| 23 | * to the device tree node for the gpt device (typically in the .dts file | 29 | * to the device tree node for the gpt device (typically in the .dts file |
| @@ -46,17 +52,24 @@ | |||
| 46 | * the output mode. This driver does not change the output mode setting. | 52 | * the output mode. This driver does not change the output mode setting. |
| 47 | */ | 53 | */ |
| 48 | 54 | ||
| 55 | #include <linux/device.h> | ||
| 49 | #include <linux/irq.h> | 56 | #include <linux/irq.h> |
| 50 | #include <linux/interrupt.h> | 57 | #include <linux/interrupt.h> |
| 51 | #include <linux/io.h> | 58 | #include <linux/io.h> |
| 59 | #include <linux/list.h> | ||
| 60 | #include <linux/mutex.h> | ||
| 52 | #include <linux/of.h> | 61 | #include <linux/of.h> |
| 53 | #include <linux/of_platform.h> | 62 | #include <linux/of_platform.h> |
| 54 | #include <linux/of_gpio.h> | 63 | #include <linux/of_gpio.h> |
| 55 | #include <linux/kernel.h> | 64 | #include <linux/kernel.h> |
| 65 | #include <linux/watchdog.h> | ||
| 66 | #include <linux/miscdevice.h> | ||
| 67 | #include <linux/uaccess.h> | ||
| 68 | #include <asm/div64.h> | ||
| 56 | #include <asm/mpc52xx.h> | 69 | #include <asm/mpc52xx.h> |
| 57 | 70 | ||
| 58 | MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); | 71 | MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); |
| 59 | MODULE_AUTHOR("Sascha Hauer, Grant Likely"); | 72 | MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß"); |
| 60 | MODULE_LICENSE("GPL"); | 73 | MODULE_LICENSE("GPL"); |
| 61 | 74 | ||
| 62 | /** | 75 | /** |
| @@ -66,18 +79,27 @@ MODULE_LICENSE("GPL"); | |||
| 66 | * @lock: spinlock to coordinate between different functions. | 79 | * @lock: spinlock to coordinate between different functions. |
| 67 | * @of_gc: of_gpio_chip instance structure; used when GPIO is enabled | 80 | * @of_gc: of_gpio_chip instance structure; used when GPIO is enabled |
| 68 | * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported | 81 | * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported |
| 82 | * @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates | ||
| 83 | * if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates | ||
| 84 | * if the timer is actively used as wdt which blocks gpt functions | ||
| 69 | */ | 85 | */ |
| 70 | struct mpc52xx_gpt_priv { | 86 | struct mpc52xx_gpt_priv { |
| 87 | struct list_head list; /* List of all GPT devices */ | ||
| 71 | struct device *dev; | 88 | struct device *dev; |
| 72 | struct mpc52xx_gpt __iomem *regs; | 89 | struct mpc52xx_gpt __iomem *regs; |
| 73 | spinlock_t lock; | 90 | spinlock_t lock; |
| 74 | struct irq_host *irqhost; | 91 | struct irq_host *irqhost; |
| 92 | u32 ipb_freq; | ||
| 93 | u8 wdt_mode; | ||
| 75 | 94 | ||
| 76 | #if defined(CONFIG_GPIOLIB) | 95 | #if defined(CONFIG_GPIOLIB) |
| 77 | struct of_gpio_chip of_gc; | 96 | struct of_gpio_chip of_gc; |
| 78 | #endif | 97 | #endif |
| 79 | }; | 98 | }; |
| 80 | 99 | ||
| 100 | LIST_HEAD(mpc52xx_gpt_list); | ||
| 101 | DEFINE_MUTEX(mpc52xx_gpt_list_mutex); | ||
| 102 | |||
| 81 | #define MPC52xx_GPT_MODE_MS_MASK (0x07) | 103 | #define MPC52xx_GPT_MODE_MS_MASK (0x07) |
| 82 | #define MPC52xx_GPT_MODE_MS_IC (0x01) | 104 | #define MPC52xx_GPT_MODE_MS_IC (0x01) |
| 83 | #define MPC52xx_GPT_MODE_MS_OC (0x02) | 105 | #define MPC52xx_GPT_MODE_MS_OC (0x02) |
| @@ -88,15 +110,25 @@ struct mpc52xx_gpt_priv { | |||
| 88 | #define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) | 110 | #define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) |
| 89 | #define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) | 111 | #define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) |
| 90 | 112 | ||
| 113 | #define MPC52xx_GPT_MODE_COUNTER_ENABLE (0x1000) | ||
| 114 | #define MPC52xx_GPT_MODE_CONTINUOUS (0x0400) | ||
| 115 | #define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200) | ||
| 91 | #define MPC52xx_GPT_MODE_IRQ_EN (0x0100) | 116 | #define MPC52xx_GPT_MODE_IRQ_EN (0x0100) |
| 117 | #define MPC52xx_GPT_MODE_WDT_EN (0x8000) | ||
| 92 | 118 | ||
| 93 | #define MPC52xx_GPT_MODE_ICT_MASK (0x030000) | 119 | #define MPC52xx_GPT_MODE_ICT_MASK (0x030000) |
| 94 | #define MPC52xx_GPT_MODE_ICT_RISING (0x010000) | 120 | #define MPC52xx_GPT_MODE_ICT_RISING (0x010000) |
| 95 | #define MPC52xx_GPT_MODE_ICT_FALLING (0x020000) | 121 | #define MPC52xx_GPT_MODE_ICT_FALLING (0x020000) |
| 96 | #define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000) | 122 | #define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000) |
| 97 | 123 | ||
| 124 | #define MPC52xx_GPT_MODE_WDT_PING (0xa5) | ||
| 125 | |||
| 98 | #define MPC52xx_GPT_STATUS_IRQMASK (0x000f) | 126 | #define MPC52xx_GPT_STATUS_IRQMASK (0x000f) |
| 99 | 127 | ||
| 128 | #define MPC52xx_GPT_CAN_WDT (1 << 0) | ||
| 129 | #define MPC52xx_GPT_IS_WDT (1 << 1) | ||
| 130 | |||
| 131 | |||
| 100 | /* --------------------------------------------------------------------- | 132 | /* --------------------------------------------------------------------- |
| 101 | * Cascaded interrupt controller hooks | 133 | * Cascaded interrupt controller hooks |
| 102 | */ | 134 | */ |
| @@ -190,7 +222,7 @@ static int mpc52xx_gpt_irq_xlate(struct irq_host *h, struct device_node *ct, | |||
| 190 | 222 | ||
| 191 | dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); | 223 | dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); |
| 192 | 224 | ||
| 193 | if ((intsize < 1) || (intspec[0] < 1) || (intspec[0] > 3)) { | 225 | if ((intsize < 1) || (intspec[0] > 3)) { |
| 194 | dev_err(gpt->dev, "bad irq specifier in %s\n", ct->full_name); | 226 | dev_err(gpt->dev, "bad irq specifier in %s\n", ct->full_name); |
| 195 | return -EINVAL; | 227 | return -EINVAL; |
| 196 | } | 228 | } |
| @@ -211,13 +243,11 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) | |||
| 211 | { | 243 | { |
| 212 | int cascade_virq; | 244 | int cascade_virq; |
| 213 | unsigned long flags; | 245 | unsigned long flags; |
| 214 | 246 | u32 mode; | |
| 215 | /* Only setup cascaded IRQ if device tree claims the GPT is | ||
| 216 | * an interrupt controller */ | ||
| 217 | if (!of_find_property(node, "interrupt-controller", NULL)) | ||
| 218 | return; | ||
| 219 | 247 | ||
| 220 | cascade_virq = irq_of_parse_and_map(node, 0); | 248 | cascade_virq = irq_of_parse_and_map(node, 0); |
| 249 | if (!cascade_virq) | ||
| 250 | return; | ||
| 221 | 251 | ||
| 222 | gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1, | 252 | gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1, |
| 223 | &mpc52xx_gpt_irq_ops, -1); | 253 | &mpc52xx_gpt_irq_ops, -1); |
| @@ -227,14 +257,16 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) | |||
| 227 | } | 257 | } |
| 228 | 258 | ||
| 229 | gpt->irqhost->host_data = gpt; | 259 | gpt->irqhost->host_data = gpt; |
| 230 | |||
| 231 | set_irq_data(cascade_virq, gpt); | 260 | set_irq_data(cascade_virq, gpt); |
| 232 | set_irq_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); | 261 | set_irq_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); |
| 233 | 262 | ||
| 234 | /* Set to Input Capture mode */ | 263 | /* If the GPT is currently disabled, then change it to be in Input |
| 264 | * Capture mode. If the mode is non-zero, then the pin could be | ||
| 265 | * already in use for something. */ | ||
| 235 | spin_lock_irqsave(&gpt->lock, flags); | 266 | spin_lock_irqsave(&gpt->lock, flags); |
| 236 | clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK, | 267 | mode = in_be32(&gpt->regs->mode); |
| 237 | MPC52xx_GPT_MODE_MS_IC); | 268 | if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0) |
| 269 | out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC); | ||
| 238 | spin_unlock_irqrestore(&gpt->lock, flags); | 270 | spin_unlock_irqrestore(&gpt->lock, flags); |
| 239 | 271 | ||
| 240 | dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); | 272 | dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); |
| @@ -335,6 +367,354 @@ static void | |||
| 335 | mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { } | 367 | mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { } |
| 336 | #endif /* defined(CONFIG_GPIOLIB) */ | 368 | #endif /* defined(CONFIG_GPIOLIB) */ |
| 337 | 369 | ||
| 370 | /*********************************************************************** | ||
| 371 | * Timer API | ||
| 372 | */ | ||
| 373 | |||
| 374 | /** | ||
| 375 | * mpc52xx_gpt_from_irq - Return the GPT device associated with an IRQ number | ||
| 376 | * @irq: irq of timer. | ||
| 377 | */ | ||
| 378 | struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) | ||
| 379 | { | ||
| 380 | struct mpc52xx_gpt_priv *gpt; | ||
| 381 | struct list_head *pos; | ||
| 382 | |||
| 383 | /* Iterate over the list of timers looking for a matching device */ | ||
| 384 | mutex_lock(&mpc52xx_gpt_list_mutex); | ||
| 385 | list_for_each(pos, &mpc52xx_gpt_list) { | ||
| 386 | gpt = container_of(pos, struct mpc52xx_gpt_priv, list); | ||
| 387 | if (gpt->irqhost && irq == irq_linear_revmap(gpt->irqhost, 0)) { | ||
| 388 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
| 389 | return gpt; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
| 393 | |||
| 394 | return NULL; | ||
| 395 | } | ||
| 396 | EXPORT_SYMBOL(mpc52xx_gpt_from_irq); | ||
| 397 | |||
| 398 | static int mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv *gpt, u64 period, | ||
| 399 | int continuous, int as_wdt) | ||
| 400 | { | ||
| 401 | u32 clear, set; | ||
| 402 | u64 clocks; | ||
| 403 | u32 prescale; | ||
| 404 | unsigned long flags; | ||
| 405 | |||
| 406 | clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS; | ||
| 407 | set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE; | ||
| 408 | if (as_wdt) { | ||
| 409 | clear |= MPC52xx_GPT_MODE_IRQ_EN; | ||
| 410 | set |= MPC52xx_GPT_MODE_WDT_EN; | ||
| 411 | } else if (continuous) | ||
| 412 | set |= MPC52xx_GPT_MODE_CONTINUOUS; | ||
| 413 | |||
| 414 | /* Determine the number of clocks in the requested period. 64 bit | ||
| 415 | * arithmatic is done here to preserve the precision until the value | ||
| 416 | * is scaled back down into the u32 range. Period is in 'ns', bus | ||
| 417 | * frequency is in Hz. */ | ||
| 418 | clocks = period * (u64)gpt->ipb_freq; | ||
| 419 | do_div(clocks, 1000000000); /* Scale it down to ns range */ | ||
| 420 | |||
| 421 | /* This device cannot handle a clock count greater than 32 bits */ | ||
| 422 | if (clocks > 0xffffffff) | ||
| 423 | return -EINVAL; | ||
| 424 | |||
| 425 | /* Calculate the prescaler and count values from the clocks value. | ||
| 426 | * 'clocks' is the number of clock ticks in the period. The timer | ||
| 427 | * has 16 bit precision and a 16 bit prescaler. Prescaler is | ||
| 428 | * calculated by integer dividing the clocks by 0x10000 (shifting | ||
| 429 | * down 16 bits) to obtain the smallest possible divisor for clocks | ||
| 430 | * to get a 16 bit count value. | ||
| 431 | * | ||
| 432 | * Note: the prescale register is '1' based, not '0' based. ie. a | ||
| 433 | * value of '1' means divide the clock by one. 0xffff divides the | ||
| 434 | * clock by 0xffff. '0x0000' does not divide by zero, but wraps | ||
| 435 | * around and divides by 0x10000. That is why prescale must be | ||
| 436 | * a u32 variable, not a u16, for this calculation. */ | ||
| 437 | prescale = (clocks >> 16) + 1; | ||
| 438 | do_div(clocks, prescale); | ||
| 439 | if (clocks > 0xffff) { | ||
| 440 | pr_err("calculation error; prescale:%x clocks:%llx\n", | ||
| 441 | prescale, clocks); | ||
| 442 | return -EINVAL; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* Set and enable the timer, reject an attempt to use a wdt as gpt */ | ||
| 446 | spin_lock_irqsave(&gpt->lock, flags); | ||
| 447 | if (as_wdt) | ||
| 448 | gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; | ||
| 449 | else if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { | ||
| 450 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
| 451 | return -EBUSY; | ||
| 452 | } | ||
| 453 | out_be32(&gpt->regs->count, prescale << 16 | clocks); | ||
| 454 | clrsetbits_be32(&gpt->regs->mode, clear, set); | ||
| 455 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
| 456 | |||
| 457 | return 0; | ||
| 458 | } | ||
| 459 | |||
| 460 | /** | ||
| 461 | * mpc52xx_gpt_start_timer - Set and enable the GPT timer | ||
| 462 | * @gpt: Pointer to gpt private data structure | ||
| 463 | * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock | ||
| 464 | * @continuous: set to 1 to make timer continuous free running | ||
| 465 | * | ||
| 466 | * An interrupt will be generated every time the timer fires | ||
| 467 | */ | ||
| 468 | int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period, | ||
| 469 | int continuous) | ||
| 470 | { | ||
| 471 | return mpc52xx_gpt_do_start(gpt, period, continuous, 0); | ||
| 472 | } | ||
| 473 | EXPORT_SYMBOL(mpc52xx_gpt_start_timer); | ||
| 474 | |||
| 475 | /** | ||
| 476 | * mpc52xx_gpt_stop_timer - Stop a gpt | ||
| 477 | * @gpt: Pointer to gpt private data structure | ||
| 478 | * | ||
| 479 | * Returns an error if attempting to stop a wdt | ||
| 480 | */ | ||
| 481 | int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt) | ||
| 482 | { | ||
| 483 | unsigned long flags; | ||
| 484 | |||
| 485 | /* reject the operation if the timer is used as watchdog (gpt 0 only) */ | ||
| 486 | spin_lock_irqsave(&gpt->lock, flags); | ||
| 487 | if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { | ||
| 488 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
| 489 | return -EBUSY; | ||
| 490 | } | ||
| 491 | |||
| 492 | clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE); | ||
| 493 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
| 494 | return 0; | ||
| 495 | } | ||
| 496 | EXPORT_SYMBOL(mpc52xx_gpt_stop_timer); | ||
| 497 | |||
| 498 | /** | ||
| 499 | * mpc52xx_gpt_timer_period - Read the timer period | ||
| 500 | * @gpt: Pointer to gpt private data structure | ||
| 501 | * | ||
| 502 | * Returns the timer period in ns | ||
| 503 | */ | ||
| 504 | u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt) | ||
| 505 | { | ||
| 506 | u64 period; | ||
| 507 | u64 prescale; | ||
| 508 | unsigned long flags; | ||
| 509 | |||
| 510 | spin_lock_irqsave(&gpt->lock, flags); | ||
| 511 | period = in_be32(&gpt->regs->count); | ||
| 512 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
| 513 | |||
| 514 | prescale = period >> 16; | ||
| 515 | period &= 0xffff; | ||
| 516 | if (prescale == 0) | ||
| 517 | prescale = 0x10000; | ||
| 518 | period = period * prescale * 1000000000ULL; | ||
| 519 | do_div(period, (u64)gpt->ipb_freq); | ||
| 520 | return period; | ||
| 521 | } | ||
| 522 | EXPORT_SYMBOL(mpc52xx_gpt_timer_period); | ||
| 523 | |||
| 524 | #if defined(CONFIG_MPC5200_WDT) | ||
| 525 | /*********************************************************************** | ||
| 526 | * Watchdog API for gpt0 | ||
| 527 | */ | ||
| 528 | |||
| 529 | #define WDT_IDENTITY "mpc52xx watchdog on GPT0" | ||
| 530 | |||
| 531 | /* wdt_is_active stores wether or not the /dev/watchdog device is opened */ | ||
| 532 | static unsigned long wdt_is_active; | ||
| 533 | |||
| 534 | /* wdt-capable gpt */ | ||
| 535 | static struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt; | ||
| 536 | |||
| 537 | /* low-level wdt functions */ | ||
| 538 | static inline void mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt) | ||
| 539 | { | ||
| 540 | unsigned long flags; | ||
| 541 | |||
| 542 | spin_lock_irqsave(&gpt_wdt->lock, flags); | ||
| 543 | out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING); | ||
| 544 | spin_unlock_irqrestore(&gpt_wdt->lock, flags); | ||
| 545 | } | ||
| 546 | |||
| 547 | /* wdt misc device api */ | ||
| 548 | static ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data, | ||
| 549 | size_t len, loff_t *ppos) | ||
| 550 | { | ||
| 551 | struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; | ||
| 552 | mpc52xx_gpt_wdt_ping(gpt_wdt); | ||
| 553 | return 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | static struct watchdog_info mpc5200_wdt_info = { | ||
| 557 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
| 558 | .identity = WDT_IDENTITY, | ||
| 559 | }; | ||
| 560 | |||
| 561 | static long mpc52xx_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 562 | unsigned long arg) | ||
| 563 | { | ||
| 564 | struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; | ||
| 565 | int __user *data = (int __user *)arg; | ||
| 566 | int timeout; | ||
| 567 | u64 real_timeout; | ||
| 568 | int ret = 0; | ||
| 569 | |||
| 570 | switch (cmd) { | ||
| 571 | case WDIOC_GETSUPPORT: | ||
| 572 | ret = copy_to_user(data, &mpc5200_wdt_info, | ||
| 573 | sizeof(mpc5200_wdt_info)); | ||
| 574 | if (ret) | ||
| 575 | ret = -EFAULT; | ||
| 576 | break; | ||
| 577 | |||
| 578 | case WDIOC_GETSTATUS: | ||
| 579 | case WDIOC_GETBOOTSTATUS: | ||
| 580 | ret = put_user(0, data); | ||
| 581 | break; | ||
| 582 | |||
| 583 | case WDIOC_KEEPALIVE: | ||
| 584 | mpc52xx_gpt_wdt_ping(gpt_wdt); | ||
| 585 | break; | ||
| 586 | |||
| 587 | case WDIOC_SETTIMEOUT: | ||
| 588 | ret = get_user(timeout, data); | ||
| 589 | if (ret) | ||
| 590 | break; | ||
| 591 | real_timeout = (u64) timeout * 1000000000ULL; | ||
| 592 | ret = mpc52xx_gpt_do_start(gpt_wdt, real_timeout, 0, 1); | ||
| 593 | if (ret) | ||
| 594 | break; | ||
| 595 | /* fall through and return the timeout */ | ||
| 596 | |||
| 597 | case WDIOC_GETTIMEOUT: | ||
| 598 | /* we need to round here as to avoid e.g. the following | ||
| 599 | * situation: | ||
| 600 | * - timeout requested is 1 second; | ||
| 601 | * - real timeout @33MHz is 999997090ns | ||
| 602 | * - the int divide by 10^9 will return 0. | ||
| 603 | */ | ||
| 604 | real_timeout = | ||
| 605 | mpc52xx_gpt_timer_period(gpt_wdt) + 500000000ULL; | ||
| 606 | do_div(real_timeout, 1000000000ULL); | ||
| 607 | timeout = (int) real_timeout; | ||
| 608 | ret = put_user(timeout, data); | ||
| 609 | break; | ||
| 610 | |||
| 611 | default: | ||
| 612 | ret = -ENOTTY; | ||
| 613 | } | ||
| 614 | return ret; | ||
| 615 | } | ||
| 616 | |||
| 617 | static int mpc52xx_wdt_open(struct inode *inode, struct file *file) | ||
| 618 | { | ||
| 619 | int ret; | ||
| 620 | |||
| 621 | /* sanity check */ | ||
| 622 | if (!mpc52xx_gpt_wdt) | ||
| 623 | return -ENODEV; | ||
| 624 | |||
| 625 | /* /dev/watchdog can only be opened once */ | ||
| 626 | if (test_and_set_bit(0, &wdt_is_active)) | ||
| 627 | return -EBUSY; | ||
| 628 | |||
| 629 | /* Set and activate the watchdog with 30 seconds timeout */ | ||
| 630 | ret = mpc52xx_gpt_do_start(mpc52xx_gpt_wdt, 30ULL * 1000000000ULL, | ||
| 631 | 0, 1); | ||
| 632 | if (ret) { | ||
| 633 | clear_bit(0, &wdt_is_active); | ||
| 634 | return ret; | ||
| 635 | } | ||
| 636 | |||
| 637 | file->private_data = mpc52xx_gpt_wdt; | ||
| 638 | return nonseekable_open(inode, file); | ||
| 639 | } | ||
| 640 | |||
| 641 | static int mpc52xx_wdt_release(struct inode *inode, struct file *file) | ||
| 642 | { | ||
| 643 | /* note: releasing the wdt in NOWAYOUT-mode does not stop it */ | ||
| 644 | #if !defined(CONFIG_WATCHDOG_NOWAYOUT) | ||
| 645 | struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; | ||
| 646 | unsigned long flags; | ||
| 647 | |||
| 648 | spin_lock_irqsave(&gpt_wdt->lock, flags); | ||
| 649 | clrbits32(&gpt_wdt->regs->mode, | ||
| 650 | MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN); | ||
| 651 | gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT; | ||
| 652 | spin_unlock_irqrestore(&gpt_wdt->lock, flags); | ||
| 653 | #endif | ||
| 654 | clear_bit(0, &wdt_is_active); | ||
| 655 | return 0; | ||
| 656 | } | ||
| 657 | |||
| 658 | |||
| 659 | static const struct file_operations mpc52xx_wdt_fops = { | ||
| 660 | .owner = THIS_MODULE, | ||
| 661 | .llseek = no_llseek, | ||
| 662 | .write = mpc52xx_wdt_write, | ||
| 663 | .unlocked_ioctl = mpc52xx_wdt_ioctl, | ||
| 664 | .open = mpc52xx_wdt_open, | ||
| 665 | .release = mpc52xx_wdt_release, | ||
| 666 | }; | ||
| 667 | |||
| 668 | static struct miscdevice mpc52xx_wdt_miscdev = { | ||
| 669 | .minor = WATCHDOG_MINOR, | ||
| 670 | .name = "watchdog", | ||
| 671 | .fops = &mpc52xx_wdt_fops, | ||
| 672 | }; | ||
| 673 | |||
| 674 | static int __devinit mpc52xx_gpt_wdt_init(void) | ||
| 675 | { | ||
| 676 | int err; | ||
| 677 | |||
| 678 | /* try to register the watchdog misc device */ | ||
| 679 | err = misc_register(&mpc52xx_wdt_miscdev); | ||
| 680 | if (err) | ||
| 681 | pr_err("%s: cannot register watchdog device\n", WDT_IDENTITY); | ||
| 682 | else | ||
| 683 | pr_info("%s: watchdog device registered\n", WDT_IDENTITY); | ||
| 684 | return err; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt, | ||
| 688 | const u32 *period) | ||
| 689 | { | ||
| 690 | u64 real_timeout; | ||
| 691 | |||
| 692 | /* remember the gpt for the wdt operation */ | ||
| 693 | mpc52xx_gpt_wdt = gpt; | ||
| 694 | |||
| 695 | /* configure the wdt if the device tree contained a timeout */ | ||
| 696 | if (!period || *period == 0) | ||
| 697 | return 0; | ||
| 698 | |||
| 699 | real_timeout = (u64) *period * 1000000000ULL; | ||
| 700 | if (mpc52xx_gpt_do_start(gpt, real_timeout, 0, 1)) | ||
| 701 | dev_warn(gpt->dev, "starting as wdt failed\n"); | ||
| 702 | else | ||
| 703 | dev_info(gpt->dev, "watchdog set to %us timeout\n", *period); | ||
| 704 | return 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | #else | ||
| 708 | |||
| 709 | static int __devinit mpc52xx_gpt_wdt_init(void) | ||
| 710 | { | ||
| 711 | return 0; | ||
| 712 | } | ||
| 713 | |||
| 714 | #define mpc52xx_gpt_wdt_setup(x, y) (0) | ||
| 715 | |||
| 716 | #endif /* CONFIG_MPC5200_WDT */ | ||
| 717 | |||
| 338 | /* --------------------------------------------------------------------- | 718 | /* --------------------------------------------------------------------- |
| 339 | * of_platform bus binding code | 719 | * of_platform bus binding code |
| 340 | */ | 720 | */ |
| @@ -349,6 +729,7 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev, | |||
| 349 | 729 | ||
| 350 | spin_lock_init(&gpt->lock); | 730 | spin_lock_init(&gpt->lock); |
| 351 | gpt->dev = &ofdev->dev; | 731 | gpt->dev = &ofdev->dev; |
| 732 | gpt->ipb_freq = mpc5xxx_get_bus_frequency(ofdev->node); | ||
| 352 | gpt->regs = of_iomap(ofdev->node, 0); | 733 | gpt->regs = of_iomap(ofdev->node, 0); |
| 353 | if (!gpt->regs) { | 734 | if (!gpt->regs) { |
| 354 | kfree(gpt); | 735 | kfree(gpt); |
| @@ -360,6 +741,26 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev, | |||
| 360 | mpc52xx_gpt_gpio_setup(gpt, ofdev->node); | 741 | mpc52xx_gpt_gpio_setup(gpt, ofdev->node); |
| 361 | mpc52xx_gpt_irq_setup(gpt, ofdev->node); | 742 | mpc52xx_gpt_irq_setup(gpt, ofdev->node); |
| 362 | 743 | ||
| 744 | mutex_lock(&mpc52xx_gpt_list_mutex); | ||
| 745 | list_add(&gpt->list, &mpc52xx_gpt_list); | ||
| 746 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
| 747 | |||
| 748 | /* check if this device could be a watchdog */ | ||
| 749 | if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) || | ||
| 750 | of_get_property(ofdev->node, "has-wdt", NULL)) { | ||
| 751 | const u32 *on_boot_wdt; | ||
| 752 | |||
| 753 | gpt->wdt_mode = MPC52xx_GPT_CAN_WDT; | ||
| 754 | on_boot_wdt = of_get_property(ofdev->node, "fsl,wdt-on-boot", | ||
| 755 | NULL); | ||
| 756 | if (on_boot_wdt) { | ||
| 757 | dev_info(gpt->dev, "used as watchdog\n"); | ||
| 758 | gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; | ||
| 759 | } else | ||
| 760 | dev_info(gpt->dev, "can function as watchdog\n"); | ||
| 761 | mpc52xx_gpt_wdt_setup(gpt, on_boot_wdt); | ||
| 762 | } | ||
| 763 | |||
| 363 | return 0; | 764 | return 0; |
| 364 | } | 765 | } |
| 365 | 766 | ||
| @@ -394,3 +795,4 @@ static int __init mpc52xx_gpt_init(void) | |||
| 394 | 795 | ||
| 395 | /* Make sure GPIOs and IRQs get set up before anyone tries to use them */ | 796 | /* Make sure GPIOs and IRQs get set up before anyone tries to use them */ |
| 396 | subsys_initcall(mpc52xx_gpt_init); | 797 | subsys_initcall(mpc52xx_gpt_init); |
| 798 | device_initcall(mpc52xx_gpt_wdt_init); | ||
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c new file mode 100644 index 000000000000..929d017535a3 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | |||
| @@ -0,0 +1,560 @@ | |||
| 1 | /* | ||
| 2 | * LocalPlus Bus FIFO driver for the Freescale MPC52xx. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Secret Lab Technologies Ltd. | ||
| 5 | * | ||
| 6 | * This file is released under the GPLv2 | ||
| 7 | * | ||
| 8 | * Todo: | ||
| 9 | * - Add support for multiple requests to be queued. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/interrupt.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/of.h> | ||
| 15 | #include <linux/of_platform.h> | ||
| 16 | #include <linux/spinlock.h> | ||
| 17 | #include <asm/io.h> | ||
| 18 | #include <asm/prom.h> | ||
| 19 | #include <asm/mpc52xx.h> | ||
| 20 | #include <asm/time.h> | ||
| 21 | |||
| 22 | #include <sysdev/bestcomm/bestcomm.h> | ||
| 23 | #include <sysdev/bestcomm/bestcomm_priv.h> | ||
| 24 | #include <sysdev/bestcomm/gen_bd.h> | ||
| 25 | |||
| 26 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
| 27 | MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver"); | ||
| 28 | MODULE_LICENSE("GPL"); | ||
| 29 | |||
| 30 | #define LPBFIFO_REG_PACKET_SIZE (0x00) | ||
| 31 | #define LPBFIFO_REG_START_ADDRESS (0x04) | ||
| 32 | #define LPBFIFO_REG_CONTROL (0x08) | ||
| 33 | #define LPBFIFO_REG_ENABLE (0x0C) | ||
| 34 | #define LPBFIFO_REG_BYTES_DONE_STATUS (0x14) | ||
| 35 | #define LPBFIFO_REG_FIFO_DATA (0x40) | ||
| 36 | #define LPBFIFO_REG_FIFO_STATUS (0x44) | ||
| 37 | #define LPBFIFO_REG_FIFO_CONTROL (0x48) | ||
| 38 | #define LPBFIFO_REG_FIFO_ALARM (0x4C) | ||
| 39 | |||
| 40 | struct mpc52xx_lpbfifo { | ||
| 41 | struct device *dev; | ||
| 42 | phys_addr_t regs_phys; | ||
| 43 | void __iomem *regs; | ||
| 44 | int irq; | ||
| 45 | spinlock_t lock; | ||
| 46 | |||
| 47 | struct bcom_task *bcom_tx_task; | ||
| 48 | struct bcom_task *bcom_rx_task; | ||
| 49 | struct bcom_task *bcom_cur_task; | ||
| 50 | |||
| 51 | /* Current state data */ | ||
| 52 | struct mpc52xx_lpbfifo_request *req; | ||
| 53 | int dma_irqs_enabled; | ||
| 54 | }; | ||
| 55 | |||
| 56 | /* The MPC5200 has only one fifo, so only need one instance structure */ | ||
| 57 | static struct mpc52xx_lpbfifo lpbfifo; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * mpc52xx_lpbfifo_kick - Trigger the next block of data to be transfered | ||
| 61 | */ | ||
| 62 | static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req) | ||
| 63 | { | ||
| 64 | size_t transfer_size = req->size - req->pos; | ||
| 65 | struct bcom_bd *bd; | ||
| 66 | void __iomem *reg; | ||
| 67 | u32 *data; | ||
| 68 | int i; | ||
| 69 | int bit_fields; | ||
| 70 | int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); | ||
| 71 | int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; | ||
| 72 | int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; | ||
| 73 | |||
| 74 | /* Set and clear the reset bits; is good practice in User Manual */ | ||
| 75 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); | ||
| 76 | |||
| 77 | /* set master enable bit */ | ||
| 78 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001); | ||
| 79 | if (!dma) { | ||
| 80 | /* While the FIFO can be setup for transfer sizes as large as | ||
| 81 | * 16M-1, the FIFO itself is only 512 bytes deep and it does | ||
| 82 | * not generate interrupts for FIFO full events (only transfer | ||
| 83 | * complete will raise an IRQ). Therefore when not using | ||
| 84 | * Bestcomm to drive the FIFO it needs to either be polled, or | ||
| 85 | * transfers need to constrained to the size of the fifo. | ||
| 86 | * | ||
| 87 | * This driver restricts the size of the transfer | ||
| 88 | */ | ||
| 89 | if (transfer_size > 512) | ||
| 90 | transfer_size = 512; | ||
| 91 | |||
| 92 | /* Load the FIFO with data */ | ||
| 93 | if (write) { | ||
| 94 | reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; | ||
| 95 | data = req->data + req->pos; | ||
| 96 | for (i = 0; i < transfer_size; i += 4) | ||
| 97 | out_be32(reg, *data++); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Unmask both error and completion irqs */ | ||
| 101 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301); | ||
| 102 | } else { | ||
| 103 | /* Choose the correct direction | ||
| 104 | * | ||
| 105 | * Configure the watermarks so DMA will always complete correctly. | ||
| 106 | * It may be worth experimenting with the ALARM value to see if | ||
| 107 | * there is a performance impacit. However, if it is wrong there | ||
| 108 | * is a risk of DMA not transferring the last chunk of data | ||
| 109 | */ | ||
| 110 | if (write) { | ||
| 111 | out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4); | ||
| 112 | out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7); | ||
| 113 | lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task; | ||
| 114 | } else { | ||
| 115 | out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff); | ||
| 116 | out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0); | ||
| 117 | lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task; | ||
| 118 | |||
| 119 | if (poll_dma) { | ||
| 120 | if (lpbfifo.dma_irqs_enabled) { | ||
| 121 | disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); | ||
| 122 | lpbfifo.dma_irqs_enabled = 0; | ||
| 123 | } | ||
| 124 | } else { | ||
| 125 | if (!lpbfifo.dma_irqs_enabled) { | ||
| 126 | enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); | ||
| 127 | lpbfifo.dma_irqs_enabled = 1; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task); | ||
| 133 | bd->status = transfer_size; | ||
| 134 | if (!write) { | ||
| 135 | /* | ||
| 136 | * In the DMA read case, the DMA doesn't complete, | ||
| 137 | * possibly due to incorrect watermarks in the ALARM | ||
| 138 | * and CONTROL regs. For now instead of trying to | ||
| 139 | * determine the right watermarks that will make this | ||
| 140 | * work, just increase the number of bytes the FIFO is | ||
| 141 | * expecting. | ||
| 142 | * | ||
| 143 | * When submitting another operation, the FIFO will get | ||
| 144 | * reset, so the condition of the FIFO waiting for a | ||
| 145 | * non-existent 4 bytes will get cleared. | ||
| 146 | */ | ||
| 147 | transfer_size += 4; /* BLECH! */ | ||
| 148 | } | ||
| 149 | bd->data[0] = req->data_phys + req->pos; | ||
| 150 | bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL); | ||
| 151 | |||
| 152 | /* error irq & master enabled bit */ | ||
| 153 | bit_fields = 0x00000201; | ||
| 154 | |||
| 155 | /* Unmask irqs */ | ||
| 156 | if (write && (!poll_dma)) | ||
| 157 | bit_fields |= 0x00000100; /* completion irq too */ | ||
| 158 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields); | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Set transfer size, width, chip select and READ mode */ | ||
| 162 | out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS, | ||
| 163 | req->offset + req->pos); | ||
| 164 | out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size); | ||
| 165 | |||
| 166 | bit_fields = req->cs << 24 | 0x000008; | ||
| 167 | if (!write) | ||
| 168 | bit_fields |= 0x010000; /* read mode */ | ||
| 169 | out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields); | ||
| 170 | |||
| 171 | /* Kick it off */ | ||
| 172 | out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01); | ||
| 173 | if (dma) | ||
| 174 | bcom_enable(lpbfifo.bcom_cur_task); | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO | ||
| 179 | * | ||
| 180 | * On transmit, the dma completion irq triggers before the fifo completion | ||
| 181 | * triggers. Handle the dma completion here instead of the LPB FIFO Bestcomm | ||
| 182 | * task completion irq becuase everyting is not really done until the LPB FIFO | ||
| 183 | * completion irq triggers. | ||
| 184 | * | ||
| 185 | * In other words: | ||
| 186 | * For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on | ||
| 187 | * transmit, the fifo completion irq is the "Fat Lady". The opera (or in this | ||
| 188 | * case the DMA/FIFO operation) is not finished until the "Fat Lady" sings. | ||
| 189 | * | ||
| 190 | * Reasons for entering this routine: | ||
| 191 | * 1) PIO mode rx and tx completion irq | ||
| 192 | * 2) DMA interrupt mode tx completion irq | ||
| 193 | * 3) DMA polled mode tx | ||
| 194 | * | ||
| 195 | * Exit conditions: | ||
| 196 | * 1) Transfer aborted | ||
| 197 | * 2) FIFO complete without DMA; more data to do | ||
| 198 | * 3) FIFO complete without DMA; all data transfered | ||
| 199 | * 4) FIFO complete using DMA | ||
| 200 | * | ||
| 201 | * Condition 1 can occur regardless of whether or not DMA is used. | ||
| 202 | * It requires executing the callback to report the error and exiting | ||
| 203 | * immediately. | ||
| 204 | * | ||
| 205 | * Condition 2 requires programming the FIFO with the next block of data | ||
| 206 | * | ||
| 207 | * Condition 3 requires executing the callback to report completion | ||
| 208 | * | ||
| 209 | * Condition 4 means the same as 3, except that we also retrieve the bcom | ||
| 210 | * buffer so DMA doesn't get clogged up. | ||
| 211 | * | ||
| 212 | * To make things trickier, the spinlock must be dropped before | ||
| 213 | * executing the callback, otherwise we could end up with a deadlock | ||
| 214 | * or nested spinlock condition. The out path is non-trivial, so | ||
| 215 | * extra fiddling is done to make sure all paths lead to the same | ||
| 216 | * outbound code. | ||
| 217 | */ | ||
| 218 | static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id) | ||
| 219 | { | ||
| 220 | struct mpc52xx_lpbfifo_request *req; | ||
| 221 | u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); | ||
| 222 | void __iomem *reg; | ||
| 223 | u32 *data; | ||
| 224 | int count, i; | ||
| 225 | int do_callback = 0; | ||
| 226 | u32 ts; | ||
| 227 | unsigned long flags; | ||
| 228 | int dma, write, poll_dma; | ||
| 229 | |||
| 230 | spin_lock_irqsave(&lpbfifo.lock, flags); | ||
| 231 | ts = get_tbl(); | ||
| 232 | |||
| 233 | req = lpbfifo.req; | ||
| 234 | if (!req) { | ||
| 235 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 236 | pr_err("bogus LPBFIFO IRQ\n"); | ||
| 237 | return IRQ_HANDLED; | ||
| 238 | } | ||
| 239 | |||
| 240 | dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); | ||
| 241 | write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; | ||
| 242 | poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; | ||
| 243 | |||
| 244 | if (dma && !write) { | ||
| 245 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 246 | pr_err("bogus LPBFIFO IRQ (dma and not writting)\n"); | ||
| 247 | return IRQ_HANDLED; | ||
| 248 | } | ||
| 249 | |||
| 250 | if ((status & 0x01) == 0) { | ||
| 251 | goto out; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* check abort bit */ | ||
| 255 | if (status & 0x10) { | ||
| 256 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); | ||
| 257 | do_callback = 1; | ||
| 258 | goto out; | ||
| 259 | } | ||
| 260 | |||
| 261 | /* Read result from hardware */ | ||
| 262 | count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); | ||
| 263 | count &= 0x00ffffff; | ||
| 264 | |||
| 265 | if (!dma && !write) { | ||
| 266 | /* copy the data out of the FIFO */ | ||
| 267 | reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; | ||
| 268 | data = req->data + req->pos; | ||
| 269 | for (i = 0; i < count; i += 4) | ||
| 270 | *data++ = in_be32(reg); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Update transfer position and count */ | ||
| 274 | req->pos += count; | ||
| 275 | |||
| 276 | /* Decide what to do next */ | ||
| 277 | if (req->size - req->pos) | ||
| 278 | mpc52xx_lpbfifo_kick(req); /* more work to do */ | ||
| 279 | else | ||
| 280 | do_callback = 1; | ||
| 281 | |||
| 282 | out: | ||
| 283 | /* Clear the IRQ */ | ||
| 284 | out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01); | ||
| 285 | |||
| 286 | if (dma && (status & 0x11)) { | ||
| 287 | /* | ||
| 288 | * Count the DMA as complete only when the FIFO completion | ||
| 289 | * status or abort bits are set. | ||
| 290 | * | ||
| 291 | * (status & 0x01) should always be the case except sometimes | ||
| 292 | * when using polled DMA. | ||
| 293 | * | ||
| 294 | * (status & 0x10) {transfer aborted}: This case needs more | ||
| 295 | * testing. | ||
| 296 | */ | ||
| 297 | bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); | ||
| 298 | } | ||
| 299 | req->last_byte = ((u8 *)req->data)[req->size - 1]; | ||
| 300 | |||
| 301 | /* When the do_callback flag is set; it means the transfer is finished | ||
| 302 | * so set the FIFO as idle */ | ||
| 303 | if (do_callback) | ||
| 304 | lpbfifo.req = NULL; | ||
| 305 | |||
| 306 | if (irq != 0) /* don't increment on polled case */ | ||
| 307 | req->irq_count++; | ||
| 308 | |||
| 309 | req->irq_ticks += get_tbl() - ts; | ||
| 310 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 311 | |||
| 312 | /* Spinlock is released; it is now safe to call the callback */ | ||
| 313 | if (do_callback && req->callback) | ||
| 314 | req->callback(req); | ||
| 315 | |||
| 316 | return IRQ_HANDLED; | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task | ||
| 321 | * | ||
| 322 | * Only used when receiving data. | ||
| 323 | */ | ||
| 324 | static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id) | ||
| 325 | { | ||
| 326 | struct mpc52xx_lpbfifo_request *req; | ||
| 327 | unsigned long flags; | ||
| 328 | u32 status; | ||
| 329 | u32 ts; | ||
| 330 | |||
| 331 | spin_lock_irqsave(&lpbfifo.lock, flags); | ||
| 332 | ts = get_tbl(); | ||
| 333 | |||
| 334 | req = lpbfifo.req; | ||
| 335 | if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) { | ||
| 336 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 337 | return IRQ_HANDLED; | ||
| 338 | } | ||
| 339 | |||
| 340 | if (irq != 0) /* don't increment on polled case */ | ||
| 341 | req->irq_count++; | ||
| 342 | |||
| 343 | if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) { | ||
| 344 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 345 | |||
| 346 | req->buffer_not_done_cnt++; | ||
| 347 | if ((req->buffer_not_done_cnt % 1000) == 0) | ||
| 348 | pr_err("transfer stalled\n"); | ||
| 349 | |||
| 350 | return IRQ_HANDLED; | ||
| 351 | } | ||
| 352 | |||
| 353 | bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); | ||
| 354 | |||
| 355 | req->last_byte = ((u8 *)req->data)[req->size - 1]; | ||
| 356 | |||
| 357 | req->pos = status & 0x00ffffff; | ||
| 358 | |||
| 359 | /* Mark the FIFO as idle */ | ||
| 360 | lpbfifo.req = NULL; | ||
| 361 | |||
| 362 | /* Release the lock before calling out to the callback. */ | ||
| 363 | req->irq_ticks += get_tbl() - ts; | ||
| 364 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 365 | |||
| 366 | if (req->callback) | ||
| 367 | req->callback(req); | ||
| 368 | |||
| 369 | return IRQ_HANDLED; | ||
| 370 | } | ||
| 371 | |||
| 372 | /** | ||
| 373 | * mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion | ||
| 374 | */ | ||
| 375 | void mpc52xx_lpbfifo_poll(void) | ||
| 376 | { | ||
| 377 | struct mpc52xx_lpbfifo_request *req = lpbfifo.req; | ||
| 378 | int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); | ||
| 379 | int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; | ||
| 380 | |||
| 381 | /* | ||
| 382 | * For more information, see comments on the "Fat Lady" | ||
| 383 | */ | ||
| 384 | if (dma && write) | ||
| 385 | mpc52xx_lpbfifo_irq(0, NULL); | ||
| 386 | else | ||
| 387 | mpc52xx_lpbfifo_bcom_irq(0, NULL); | ||
| 388 | } | ||
| 389 | EXPORT_SYMBOL(mpc52xx_lpbfifo_poll); | ||
| 390 | |||
| 391 | /** | ||
| 392 | * mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request. | ||
| 393 | * @req: Pointer to request structure | ||
| 394 | */ | ||
| 395 | int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req) | ||
| 396 | { | ||
| 397 | unsigned long flags; | ||
| 398 | |||
| 399 | if (!lpbfifo.regs) | ||
| 400 | return -ENODEV; | ||
| 401 | |||
| 402 | spin_lock_irqsave(&lpbfifo.lock, flags); | ||
| 403 | |||
| 404 | /* If the req pointer is already set, then a transfer is in progress */ | ||
| 405 | if (lpbfifo.req) { | ||
| 406 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 407 | return -EBUSY; | ||
| 408 | } | ||
| 409 | |||
| 410 | /* Setup the transfer */ | ||
| 411 | lpbfifo.req = req; | ||
| 412 | req->irq_count = 0; | ||
| 413 | req->irq_ticks = 0; | ||
| 414 | req->buffer_not_done_cnt = 0; | ||
| 415 | req->pos = 0; | ||
| 416 | |||
| 417 | mpc52xx_lpbfifo_kick(req); | ||
| 418 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 419 | return 0; | ||
| 420 | } | ||
| 421 | EXPORT_SYMBOL(mpc52xx_lpbfifo_submit); | ||
| 422 | |||
| 423 | void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req) | ||
| 424 | { | ||
| 425 | unsigned long flags; | ||
| 426 | |||
| 427 | spin_lock_irqsave(&lpbfifo.lock, flags); | ||
| 428 | if (lpbfifo.req == req) { | ||
| 429 | /* Put it into reset and clear the state */ | ||
| 430 | bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task); | ||
| 431 | bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task); | ||
| 432 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); | ||
| 433 | lpbfifo.req = NULL; | ||
| 434 | } | ||
| 435 | spin_unlock_irqrestore(&lpbfifo.lock, flags); | ||
| 436 | } | ||
| 437 | EXPORT_SYMBOL(mpc52xx_lpbfifo_abort); | ||
| 438 | |||
| 439 | static int __devinit | ||
| 440 | mpc52xx_lpbfifo_probe(struct of_device *op, const struct of_device_id *match) | ||
| 441 | { | ||
| 442 | struct resource res; | ||
| 443 | int rc = -ENOMEM; | ||
| 444 | |||
| 445 | if (lpbfifo.dev != NULL) | ||
| 446 | return -ENOSPC; | ||
| 447 | |||
| 448 | lpbfifo.irq = irq_of_parse_and_map(op->node, 0); | ||
| 449 | if (!lpbfifo.irq) | ||
| 450 | return -ENODEV; | ||
| 451 | |||
| 452 | if (of_address_to_resource(op->node, 0, &res)) | ||
| 453 | return -ENODEV; | ||
| 454 | lpbfifo.regs_phys = res.start; | ||
| 455 | lpbfifo.regs = of_iomap(op->node, 0); | ||
| 456 | if (!lpbfifo.regs) | ||
| 457 | return -ENOMEM; | ||
| 458 | |||
| 459 | spin_lock_init(&lpbfifo.lock); | ||
| 460 | |||
| 461 | /* Put FIFO into reset */ | ||
| 462 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); | ||
| 463 | |||
| 464 | /* Register the interrupt handler */ | ||
| 465 | rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0, | ||
| 466 | "mpc52xx-lpbfifo", &lpbfifo); | ||
| 467 | if (rc) | ||
| 468 | goto err_irq; | ||
| 469 | |||
| 470 | /* Request the Bestcomm receive (fifo --> memory) task and IRQ */ | ||
| 471 | lpbfifo.bcom_rx_task = | ||
| 472 | bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, | ||
| 473 | BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC, | ||
| 474 | 16*1024*1024); | ||
| 475 | if (!lpbfifo.bcom_rx_task) | ||
| 476 | goto err_bcom_rx; | ||
| 477 | |||
| 478 | rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), | ||
| 479 | mpc52xx_lpbfifo_bcom_irq, 0, | ||
| 480 | "mpc52xx-lpbfifo-rx", &lpbfifo); | ||
| 481 | if (rc) | ||
| 482 | goto err_bcom_rx_irq; | ||
| 483 | |||
| 484 | /* Request the Bestcomm transmit (memory --> fifo) task and IRQ */ | ||
| 485 | lpbfifo.bcom_tx_task = | ||
| 486 | bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, | ||
| 487 | BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC); | ||
| 488 | if (!lpbfifo.bcom_tx_task) | ||
| 489 | goto err_bcom_tx; | ||
| 490 | |||
| 491 | lpbfifo.dev = &op->dev; | ||
| 492 | return 0; | ||
| 493 | |||
| 494 | err_bcom_tx: | ||
| 495 | free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); | ||
| 496 | err_bcom_rx_irq: | ||
| 497 | bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); | ||
| 498 | err_bcom_rx: | ||
| 499 | err_irq: | ||
| 500 | iounmap(lpbfifo.regs); | ||
| 501 | lpbfifo.regs = NULL; | ||
| 502 | |||
| 503 | dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n"); | ||
| 504 | return -ENODEV; | ||
| 505 | } | ||
| 506 | |||
| 507 | |||
| 508 | static int __devexit mpc52xx_lpbfifo_remove(struct of_device *op) | ||
| 509 | { | ||
| 510 | if (lpbfifo.dev != &op->dev) | ||
| 511 | return 0; | ||
| 512 | |||
| 513 | /* Put FIFO in reset */ | ||
| 514 | out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); | ||
| 515 | |||
| 516 | /* Release the bestcomm transmit task */ | ||
| 517 | free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo); | ||
| 518 | bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task); | ||
| 519 | |||
| 520 | /* Release the bestcomm receive task */ | ||
| 521 | free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); | ||
| 522 | bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); | ||
| 523 | |||
| 524 | free_irq(lpbfifo.irq, &lpbfifo); | ||
| 525 | iounmap(lpbfifo.regs); | ||
| 526 | lpbfifo.regs = NULL; | ||
| 527 | lpbfifo.dev = NULL; | ||
| 528 | |||
| 529 | return 0; | ||
| 530 | } | ||
| 531 | |||
| 532 | static struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst = { | ||
| 533 | { .compatible = "fsl,mpc5200-lpbfifo", }, | ||
| 534 | {}, | ||
| 535 | }; | ||
| 536 | |||
| 537 | static struct of_platform_driver mpc52xx_lpbfifo_driver = { | ||
| 538 | .owner = THIS_MODULE, | ||
| 539 | .name = "mpc52xx-lpbfifo", | ||
| 540 | .match_table = mpc52xx_lpbfifo_match, | ||
| 541 | .probe = mpc52xx_lpbfifo_probe, | ||
| 542 | .remove = __devexit_p(mpc52xx_lpbfifo_remove), | ||
| 543 | }; | ||
| 544 | |||
| 545 | /*********************************************************************** | ||
| 546 | * Module init/exit | ||
| 547 | */ | ||
| 548 | static int __init mpc52xx_lpbfifo_init(void) | ||
| 549 | { | ||
| 550 | pr_debug("Registering LocalPlus bus FIFO driver\n"); | ||
| 551 | return of_register_platform_driver(&mpc52xx_lpbfifo_driver); | ||
| 552 | } | ||
| 553 | module_init(mpc52xx_lpbfifo_init); | ||
| 554 | |||
| 555 | static void __exit mpc52xx_lpbfifo_exit(void) | ||
| 556 | { | ||
| 557 | pr_debug("Unregistering LocalPlus bus FIFO driver\n"); | ||
| 558 | of_unregister_platform_driver(&mpc52xx_lpbfifo_driver); | ||
| 559 | } | ||
| 560 | module_exit(mpc52xx_lpbfifo_exit); | ||
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index 0c44d560bf1a..0c7a63c1f12f 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c | |||
| @@ -22,6 +22,8 @@ | |||
| 22 | #include <linux/mmc/core.h> | 22 | #include <linux/mmc/core.h> |
| 23 | #include <linux/mmc/host.h> | 23 | #include <linux/mmc/host.h> |
| 24 | 24 | ||
| 25 | MODULE_LICENSE("GPL"); | ||
| 26 | |||
| 25 | enum { | 27 | enum { |
| 26 | CD_GPIO = 0, | 28 | CD_GPIO = 0, |
| 27 | WP_GPIO, | 29 | WP_GPIO, |
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 94058c62620a..28fce65b8594 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
| @@ -133,6 +133,14 @@ config SPI_LM70_LLP | |||
| 133 | which interfaces to an LM70 temperature sensor using | 133 | which interfaces to an LM70 temperature sensor using |
| 134 | a parallel port. | 134 | a parallel port. |
| 135 | 135 | ||
| 136 | config SPI_MPC52xx | ||
| 137 | tristate "Freescale MPC52xx SPI (non-PSC) controller support" | ||
| 138 | depends on PPC_MPC52xx && SPI | ||
| 139 | select SPI_MASTER_OF | ||
| 140 | help | ||
| 141 | This drivers supports the MPC52xx SPI controller in master SPI | ||
| 142 | mode. | ||
| 143 | |||
| 136 | config SPI_MPC52xx_PSC | 144 | config SPI_MPC52xx_PSC |
| 137 | tristate "Freescale MPC52xx PSC SPI controller" | 145 | tristate "Freescale MPC52xx PSC SPI controller" |
| 138 | depends on PPC_MPC52xx && EXPERIMENTAL | 146 | depends on PPC_MPC52xx && EXPERIMENTAL |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 21a118269cac..e3f092a9afa5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
| @@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o | |||
| 25 | obj-$(CONFIG_SPI_ORION) += orion_spi.o | 25 | obj-$(CONFIG_SPI_ORION) += orion_spi.o |
| 26 | obj-$(CONFIG_SPI_PL022) += amba-pl022.o | 26 | obj-$(CONFIG_SPI_PL022) += amba-pl022.o |
| 27 | obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o | 27 | obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o |
| 28 | obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o | ||
| 28 | obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o | 29 | obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o |
| 29 | obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o | 30 | obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o |
| 30 | obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o | 31 | 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 1b74d5ca03f3..f50c81df336a 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
| 18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
| 19 | #include <linux/of_platform.h> | 19 | #include <linux/of_platform.h> |
| 20 | #include <linux/of_spi.h> | ||
| 20 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
| 21 | #include <linux/completion.h> | 22 | #include <linux/completion.h> |
| 22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| @@ -313,11 +314,13 @@ static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) | |||
| 313 | struct mpc52xx_psc __iomem *psc = mps->psc; | 314 | struct mpc52xx_psc __iomem *psc = mps->psc; |
| 314 | struct mpc52xx_psc_fifo __iomem *fifo = mps->fifo; | 315 | struct mpc52xx_psc_fifo __iomem *fifo = mps->fifo; |
| 315 | u32 mclken_div; | 316 | u32 mclken_div; |
| 316 | int ret = 0; | 317 | int ret; |
| 317 | 318 | ||
| 318 | /* default sysclk is 512MHz */ | 319 | /* default sysclk is 512MHz */ |
| 319 | mclken_div = (mps->sysclk ? mps->sysclk : 512000000) / MCLK; | 320 | mclken_div = (mps->sysclk ? mps->sysclk : 512000000) / MCLK; |
| 320 | mpc52xx_set_psc_clkdiv(psc_id, mclken_div); | 321 | ret = mpc52xx_set_psc_clkdiv(psc_id, mclken_div); |
| 322 | if (ret) | ||
| 323 | return ret; | ||
| 321 | 324 | ||
| 322 | /* Reset the PSC into a known state */ | 325 | /* Reset the PSC into a known state */ |
| 323 | out_8(&psc->command, MPC52xx_PSC_RST_RX); | 326 | 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) | |||
| 341 | 344 | ||
| 342 | mps->bits_per_word = 8; | 345 | mps->bits_per_word = 8; |
| 343 | 346 | ||
| 344 | return ret; | 347 | return 0; |
| 345 | } | 348 | } |
| 346 | 349 | ||
| 347 | static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) | 350 | 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, | |||
| 410 | goto free_master; | 413 | goto free_master; |
| 411 | 414 | ||
| 412 | ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); | 415 | ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); |
| 413 | if (ret < 0) | 416 | if (ret < 0) { |
| 417 | dev_err(dev, "can't configure PSC! Is it capable of SPI?\n"); | ||
| 414 | goto free_irq; | 418 | goto free_irq; |
| 419 | } | ||
| 415 | 420 | ||
| 416 | spin_lock_init(&mps->lock); | 421 | spin_lock_init(&mps->lock); |
| 417 | init_completion(&mps->done); | 422 | init_completion(&mps->done); |
| @@ -464,10 +469,11 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, | |||
| 464 | const u32 *regaddr_p; | 469 | const u32 *regaddr_p; |
| 465 | u64 regaddr64, size64; | 470 | u64 regaddr64, size64; |
| 466 | s16 id = -1; | 471 | s16 id = -1; |
| 472 | int rc; | ||
| 467 | 473 | ||
| 468 | regaddr_p = of_get_address(op->node, 0, &size64, NULL); | 474 | regaddr_p = of_get_address(op->node, 0, &size64, NULL); |
| 469 | if (!regaddr_p) { | 475 | if (!regaddr_p) { |
| 470 | printk(KERN_ERR "Invalid PSC address\n"); | 476 | dev_err(&op->dev, "Invalid PSC address\n"); |
| 471 | return -EINVAL; | 477 | return -EINVAL; |
| 472 | } | 478 | } |
| 473 | regaddr64 = of_translate_address(op->node, regaddr_p); | 479 | regaddr64 = of_translate_address(op->node, regaddr_p); |
| @@ -478,15 +484,18 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, | |||
| 478 | 484 | ||
| 479 | psc_nump = of_get_property(op->node, "cell-index", NULL); | 485 | psc_nump = of_get_property(op->node, "cell-index", NULL); |
| 480 | if (!psc_nump || *psc_nump > 5) { | 486 | if (!psc_nump || *psc_nump > 5) { |
| 481 | printk(KERN_ERR "mpc52xx_psc_spi: Device node %s has invalid " | 487 | dev_err(&op->dev, "Invalid cell-index property\n"); |
| 482 | "cell-index property\n", op->node->full_name); | ||
| 483 | return -EINVAL; | 488 | return -EINVAL; |
| 484 | } | 489 | } |
| 485 | id = *psc_nump + 1; | 490 | id = *psc_nump + 1; |
| 486 | } | 491 | } |
| 487 | 492 | ||
| 488 | return mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, | 493 | rc = mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, |
| 489 | irq_of_parse_and_map(op->node, 0), id); | 494 | irq_of_parse_and_map(op->node, 0), id); |
| 495 | if (rc == 0) | ||
| 496 | of_register_spi_devices(dev_get_drvdata(&op->dev), op->node); | ||
| 497 | |||
| 498 | return rc; | ||
| 490 | } | 499 | } |
| 491 | 500 | ||
| 492 | static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) | 501 | 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 000000000000..ef8379b2c172 --- /dev/null +++ b/drivers/spi/mpc52xx_spi.c | |||
| @@ -0,0 +1,520 @@ | |||
| 1 | /* | ||
| 2 | * MPC52xx SPI bus driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | ||
| 5 | * | ||
| 6 | * This file is released under the GPLv2 | ||
| 7 | * | ||
| 8 | * This is the driver for the MPC5200's dedicated SPI controller. | ||
| 9 | * | ||
| 10 | * Note: this driver does not support the MPC5200 PSC in SPI mode. For | ||
| 11 | * that driver see drivers/spi/mpc52xx_psc_spi.c | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/of_platform.h> | ||
| 18 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/delay.h> | ||
| 20 | #include <linux/spi/spi.h> | ||
| 21 | #include <linux/spi/mpc52xx_spi.h> | ||
| 22 | #include <linux/of_spi.h> | ||
| 23 | #include <linux/io.h> | ||
| 24 | #include <asm/time.h> | ||
| 25 | #include <asm/mpc52xx.h> | ||
| 26 | |||
| 27 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
| 28 | MODULE_DESCRIPTION("MPC52xx SPI (non-PSC) Driver"); | ||
| 29 | MODULE_LICENSE("GPL"); | ||
| 30 | |||
| 31 | /* Register offsets */ | ||
| 32 | #define SPI_CTRL1 0x00 | ||
| 33 | #define SPI_CTRL1_SPIE (1 << 7) | ||
| 34 | #define SPI_CTRL1_SPE (1 << 6) | ||
| 35 | #define SPI_CTRL1_MSTR (1 << 4) | ||
| 36 | #define SPI_CTRL1_CPOL (1 << 3) | ||
| 37 | #define SPI_CTRL1_CPHA (1 << 2) | ||
| 38 | #define SPI_CTRL1_SSOE (1 << 1) | ||
| 39 | #define SPI_CTRL1_LSBFE (1 << 0) | ||
| 40 | |||
| 41 | #define SPI_CTRL2 0x01 | ||
| 42 | #define SPI_BRR 0x04 | ||
| 43 | |||
| 44 | #define SPI_STATUS 0x05 | ||
| 45 | #define SPI_STATUS_SPIF (1 << 7) | ||
| 46 | #define SPI_STATUS_WCOL (1 << 6) | ||
| 47 | #define SPI_STATUS_MODF (1 << 4) | ||
| 48 | |||
| 49 | #define SPI_DATA 0x09 | ||
| 50 | #define SPI_PORTDATA 0x0d | ||
| 51 | #define SPI_DATADIR 0x10 | ||
| 52 | |||
| 53 | /* FSM state return values */ | ||
| 54 | #define FSM_STOP 0 /* Nothing more for the state machine to */ | ||
| 55 | /* do. If something interesting happens */ | ||
| 56 | /* then and IRQ will be received */ | ||
| 57 | #define FSM_POLL 1 /* need to poll for completion, an IRQ is */ | ||
| 58 | /* not expected */ | ||
| 59 | #define FSM_CONTINUE 2 /* Keep iterating the state machine */ | ||
| 60 | |||
| 61 | /* Driver internal data */ | ||
| 62 | struct mpc52xx_spi { | ||
| 63 | struct spi_master *master; | ||
| 64 | u32 sysclk; | ||
| 65 | void __iomem *regs; | ||
| 66 | int irq0; /* MODF irq */ | ||
| 67 | int irq1; /* SPIF irq */ | ||
| 68 | int ipb_freq; | ||
| 69 | |||
| 70 | /* Statistics */ | ||
| 71 | int msg_count; | ||
| 72 | int wcol_count; | ||
| 73 | int wcol_ticks; | ||
| 74 | u32 wcol_tx_timestamp; | ||
| 75 | int modf_count; | ||
| 76 | int byte_count; | ||
| 77 | |||
| 78 | struct list_head queue; /* queue of pending messages */ | ||
| 79 | spinlock_t lock; | ||
| 80 | struct work_struct work; | ||
| 81 | |||
| 82 | |||
| 83 | /* Details of current transfer (length, and buffer pointers) */ | ||
| 84 | struct spi_message *message; /* current message */ | ||
| 85 | struct spi_transfer *transfer; /* current transfer */ | ||
| 86 | int (*state)(int irq, struct mpc52xx_spi *ms, u8 status, u8 data); | ||
| 87 | int len; | ||
| 88 | int timestamp; | ||
| 89 | u8 *rx_buf; | ||
| 90 | const u8 *tx_buf; | ||
| 91 | int cs_change; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /* | ||
| 95 | * CS control function | ||
| 96 | */ | ||
| 97 | static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value) | ||
| 98 | { | ||
| 99 | out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Start a new transfer. This is called both by the idle state | ||
| 104 | * for the first transfer in a message, and by the wait state when the | ||
| 105 | * previous transfer in a message is complete. | ||
| 106 | */ | ||
| 107 | static void mpc52xx_spi_start_transfer(struct mpc52xx_spi *ms) | ||
| 108 | { | ||
| 109 | ms->rx_buf = ms->transfer->rx_buf; | ||
| 110 | ms->tx_buf = ms->transfer->tx_buf; | ||
| 111 | ms->len = ms->transfer->len; | ||
| 112 | |||
| 113 | /* Activate the chip select */ | ||
| 114 | if (ms->cs_change) | ||
| 115 | mpc52xx_spi_chipsel(ms, 1); | ||
| 116 | ms->cs_change = ms->transfer->cs_change; | ||
| 117 | |||
| 118 | /* Write out the first byte */ | ||
| 119 | ms->wcol_tx_timestamp = get_tbl(); | ||
| 120 | if (ms->tx_buf) | ||
| 121 | out_8(ms->regs + SPI_DATA, *ms->tx_buf++); | ||
| 122 | else | ||
| 123 | out_8(ms->regs + SPI_DATA, 0); | ||
| 124 | } | ||
| 125 | |||
| 126 | /* Forward declaration of state handlers */ | ||
| 127 | static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, | ||
| 128 | u8 status, u8 data); | ||
| 129 | static int mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, | ||
| 130 | u8 status, u8 data); | ||
| 131 | |||
| 132 | /* | ||
| 133 | * IDLE state | ||
| 134 | * | ||
| 135 | * No transfers are in progress; if another transfer is pending then retrieve | ||
| 136 | * it and kick it off. Otherwise, stop processing the state machine | ||
| 137 | */ | ||
| 138 | static int | ||
| 139 | mpc52xx_spi_fsmstate_idle(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) | ||
| 140 | { | ||
| 141 | struct spi_device *spi; | ||
| 142 | int spr, sppr; | ||
| 143 | u8 ctrl1; | ||
| 144 | |||
| 145 | if (status && (irq != NO_IRQ)) | ||
| 146 | dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n", | ||
| 147 | status); | ||
| 148 | |||
| 149 | /* Check if there is another transfer waiting. */ | ||
| 150 | if (list_empty(&ms->queue)) | ||
| 151 | return FSM_STOP; | ||
| 152 | |||
| 153 | /* get the head of the queue */ | ||
| 154 | ms->message = list_first_entry(&ms->queue, struct spi_message, queue); | ||
| 155 | list_del_init(&ms->message->queue); | ||
| 156 | |||
| 157 | /* Setup the controller parameters */ | ||
| 158 | ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR; | ||
| 159 | spi = ms->message->spi; | ||
| 160 | if (spi->mode & SPI_CPHA) | ||
| 161 | ctrl1 |= SPI_CTRL1_CPHA; | ||
| 162 | if (spi->mode & SPI_CPOL) | ||
| 163 | ctrl1 |= SPI_CTRL1_CPOL; | ||
| 164 | if (spi->mode & SPI_LSB_FIRST) | ||
| 165 | ctrl1 |= SPI_CTRL1_LSBFE; | ||
| 166 | out_8(ms->regs + SPI_CTRL1, ctrl1); | ||
| 167 | |||
| 168 | /* Setup the controller speed */ | ||
| 169 | /* minimum divider is '2'. Also, add '1' to force rounding the | ||
| 170 | * divider up. */ | ||
| 171 | sppr = ((ms->ipb_freq / ms->message->spi->max_speed_hz) + 1) >> 1; | ||
| 172 | spr = 0; | ||
| 173 | if (sppr < 1) | ||
| 174 | sppr = 1; | ||
| 175 | while (((sppr - 1) & ~0x7) != 0) { | ||
| 176 | sppr = (sppr + 1) >> 1; /* add '1' to force rounding up */ | ||
| 177 | spr++; | ||
| 178 | } | ||
| 179 | sppr--; /* sppr quantity in register is offset by 1 */ | ||
| 180 | if (spr > 7) { | ||
| 181 | /* Don't overrun limits of SPI baudrate register */ | ||
| 182 | spr = 7; | ||
| 183 | sppr = 7; | ||
| 184 | } | ||
| 185 | out_8(ms->regs + SPI_BRR, sppr << 4 | spr); /* Set speed */ | ||
| 186 | |||
| 187 | ms->cs_change = 1; | ||
| 188 | ms->transfer = container_of(ms->message->transfers.next, | ||
| 189 | struct spi_transfer, transfer_list); | ||
| 190 | |||
| 191 | mpc52xx_spi_start_transfer(ms); | ||
| 192 | ms->state = mpc52xx_spi_fsmstate_transfer; | ||
| 193 | |||
| 194 | return FSM_CONTINUE; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* | ||
| 198 | * TRANSFER state | ||
| 199 | * | ||
| 200 | * In the middle of a transfer. If the SPI core has completed processing | ||
| 201 | * a byte, then read out the received data and write out the next byte | ||
| 202 | * (unless this transfer is finished; in which case go on to the wait | ||
| 203 | * state) | ||
| 204 | */ | ||
| 205 | static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, | ||
| 206 | u8 status, u8 data) | ||
| 207 | { | ||
| 208 | if (!status) | ||
| 209 | return ms->irq0 ? FSM_STOP : FSM_POLL; | ||
| 210 | |||
| 211 | if (status & SPI_STATUS_WCOL) { | ||
| 212 | /* The SPI controller is stoopid. At slower speeds, it may | ||
| 213 | * raise the SPIF flag before the state machine is actually | ||
| 214 | * finished, which causes a collision (internal to the state | ||
| 215 | * machine only). The manual recommends inserting a delay | ||
| 216 | * between receiving the interrupt and sending the next byte, | ||
| 217 | * but it can also be worked around simply by retrying the | ||
| 218 | * transfer which is what we do here. */ | ||
| 219 | ms->wcol_count++; | ||
| 220 | ms->wcol_ticks += get_tbl() - ms->wcol_tx_timestamp; | ||
| 221 | ms->wcol_tx_timestamp = get_tbl(); | ||
| 222 | data = 0; | ||
| 223 | if (ms->tx_buf) | ||
| 224 | data = *(ms->tx_buf-1); | ||
| 225 | out_8(ms->regs + SPI_DATA, data); /* try again */ | ||
| 226 | return FSM_CONTINUE; | ||
| 227 | } else if (status & SPI_STATUS_MODF) { | ||
| 228 | ms->modf_count++; | ||
| 229 | dev_err(&ms->master->dev, "mode fault\n"); | ||
| 230 | mpc52xx_spi_chipsel(ms, 0); | ||
| 231 | ms->message->status = -EIO; | ||
| 232 | ms->message->complete(ms->message->context); | ||
| 233 | ms->state = mpc52xx_spi_fsmstate_idle; | ||
| 234 | return FSM_CONTINUE; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* Read data out of the spi device */ | ||
| 238 | ms->byte_count++; | ||
| 239 | if (ms->rx_buf) | ||
| 240 | *ms->rx_buf++ = data; | ||
| 241 | |||
| 242 | /* Is the transfer complete? */ | ||
| 243 | ms->len--; | ||
| 244 | if (ms->len == 0) { | ||
| 245 | ms->timestamp = get_tbl(); | ||
| 246 | ms->timestamp += ms->transfer->delay_usecs * tb_ticks_per_usec; | ||
| 247 | ms->state = mpc52xx_spi_fsmstate_wait; | ||
| 248 | return FSM_CONTINUE; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* Write out the next byte */ | ||
| 252 | ms->wcol_tx_timestamp = get_tbl(); | ||
| 253 | if (ms->tx_buf) | ||
| 254 | out_8(ms->regs + SPI_DATA, *ms->tx_buf++); | ||
| 255 | else | ||
| 256 | out_8(ms->regs + SPI_DATA, 0); | ||
| 257 | |||
| 258 | return FSM_CONTINUE; | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * WAIT state | ||
| 263 | * | ||
| 264 | * A transfer has completed; need to wait for the delay period to complete | ||
| 265 | * before starting the next transfer | ||
| 266 | */ | ||
| 267 | static int | ||
| 268 | mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) | ||
| 269 | { | ||
| 270 | if (status && irq) | ||
| 271 | dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n", | ||
| 272 | status); | ||
| 273 | |||
| 274 | if (((int)get_tbl()) - ms->timestamp < 0) | ||
| 275 | return FSM_POLL; | ||
| 276 | |||
| 277 | ms->message->actual_length += ms->transfer->len; | ||
| 278 | |||
| 279 | /* Check if there is another transfer in this message. If there | ||
| 280 | * aren't then deactivate CS, notify sender, and drop back to idle | ||
| 281 | * to start the next message. */ | ||
| 282 | if (ms->transfer->transfer_list.next == &ms->message->transfers) { | ||
| 283 | ms->msg_count++; | ||
| 284 | mpc52xx_spi_chipsel(ms, 0); | ||
| 285 | ms->message->status = 0; | ||
| 286 | ms->message->complete(ms->message->context); | ||
| 287 | ms->state = mpc52xx_spi_fsmstate_idle; | ||
| 288 | return FSM_CONTINUE; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* There is another transfer; kick it off */ | ||
| 292 | |||
| 293 | if (ms->cs_change) | ||
| 294 | mpc52xx_spi_chipsel(ms, 0); | ||
| 295 | |||
| 296 | ms->transfer = container_of(ms->transfer->transfer_list.next, | ||
| 297 | struct spi_transfer, transfer_list); | ||
| 298 | mpc52xx_spi_start_transfer(ms); | ||
| 299 | ms->state = mpc52xx_spi_fsmstate_transfer; | ||
| 300 | return FSM_CONTINUE; | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 304 | * mpc52xx_spi_fsm_process - Finite State Machine iteration function | ||
| 305 | * @irq: irq number that triggered the FSM or 0 for polling | ||
| 306 | * @ms: pointer to mpc52xx_spi driver data | ||
| 307 | */ | ||
| 308 | static void mpc52xx_spi_fsm_process(int irq, struct mpc52xx_spi *ms) | ||
| 309 | { | ||
| 310 | int rc = FSM_CONTINUE; | ||
| 311 | u8 status, data; | ||
| 312 | |||
| 313 | while (rc == FSM_CONTINUE) { | ||
| 314 | /* Interrupt cleared by read of STATUS followed by | ||
| 315 | * read of DATA registers */ | ||
| 316 | status = in_8(ms->regs + SPI_STATUS); | ||
| 317 | data = in_8(ms->regs + SPI_DATA); | ||
| 318 | rc = ms->state(irq, ms, status, data); | ||
| 319 | } | ||
| 320 | |||
| 321 | if (rc == FSM_POLL) | ||
| 322 | schedule_work(&ms->work); | ||
| 323 | } | ||
| 324 | |||
| 325 | /** | ||
| 326 | * mpc52xx_spi_irq - IRQ handler | ||
| 327 | */ | ||
| 328 | static irqreturn_t mpc52xx_spi_irq(int irq, void *_ms) | ||
| 329 | { | ||
| 330 | struct mpc52xx_spi *ms = _ms; | ||
| 331 | spin_lock(&ms->lock); | ||
| 332 | mpc52xx_spi_fsm_process(irq, ms); | ||
| 333 | spin_unlock(&ms->lock); | ||
| 334 | return IRQ_HANDLED; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * mpc52xx_spi_wq - Workqueue function for polling the state machine | ||
| 339 | */ | ||
| 340 | static void mpc52xx_spi_wq(struct work_struct *work) | ||
| 341 | { | ||
| 342 | struct mpc52xx_spi *ms = container_of(work, struct mpc52xx_spi, work); | ||
| 343 | unsigned long flags; | ||
| 344 | |||
| 345 | spin_lock_irqsave(&ms->lock, flags); | ||
| 346 | mpc52xx_spi_fsm_process(0, ms); | ||
| 347 | spin_unlock_irqrestore(&ms->lock, flags); | ||
| 348 | } | ||
| 349 | |||
| 350 | /* | ||
| 351 | * spi_master ops | ||
| 352 | */ | ||
| 353 | |||
| 354 | static int mpc52xx_spi_setup(struct spi_device *spi) | ||
| 355 | { | ||
| 356 | if (spi->bits_per_word % 8) | ||
| 357 | return -EINVAL; | ||
| 358 | |||
| 359 | if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) | ||
| 360 | return -EINVAL; | ||
| 361 | |||
| 362 | if (spi->chip_select >= spi->master->num_chipselect) | ||
| 363 | return -EINVAL; | ||
| 364 | |||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) | ||
| 369 | { | ||
| 370 | struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master); | ||
| 371 | unsigned long flags; | ||
| 372 | |||
| 373 | m->actual_length = 0; | ||
| 374 | m->status = -EINPROGRESS; | ||
| 375 | |||
| 376 | spin_lock_irqsave(&ms->lock, flags); | ||
| 377 | list_add_tail(&m->queue, &ms->queue); | ||
| 378 | spin_unlock_irqrestore(&ms->lock, flags); | ||
| 379 | schedule_work(&ms->work); | ||
| 380 | |||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* | ||
| 385 | * OF Platform Bus Binding | ||
| 386 | */ | ||
| 387 | static int __devinit mpc52xx_spi_probe(struct of_device *op, | ||
| 388 | const struct of_device_id *match) | ||
| 389 | { | ||
| 390 | struct spi_master *master; | ||
| 391 | struct mpc52xx_spi *ms; | ||
| 392 | void __iomem *regs; | ||
| 393 | int rc; | ||
| 394 | |||
| 395 | /* MMIO registers */ | ||
| 396 | dev_dbg(&op->dev, "probing mpc5200 SPI device\n"); | ||
| 397 | regs = of_iomap(op->node, 0); | ||
| 398 | if (!regs) | ||
| 399 | return -ENODEV; | ||
| 400 | |||
| 401 | /* initialize the device */ | ||
| 402 | out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR); | ||
| 403 | out_8(regs + SPI_CTRL2, 0x0); | ||
| 404 | out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */ | ||
| 405 | out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */ | ||
| 406 | |||
| 407 | /* Clear the status register and re-read it to check for a MODF | ||
| 408 | * failure. This driver cannot currently handle multiple masters | ||
| 409 | * on the SPI bus. This fault will also occur if the SPI signals | ||
| 410 | * are not connected to any pins (port_config setting) */ | ||
| 411 | in_8(regs + SPI_STATUS); | ||
| 412 | in_8(regs + SPI_DATA); | ||
| 413 | if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) { | ||
| 414 | dev_err(&op->dev, "mode fault; is port_config correct?\n"); | ||
| 415 | rc = -EIO; | ||
| 416 | goto err_init; | ||
| 417 | } | ||
| 418 | |||
| 419 | dev_dbg(&op->dev, "allocating spi_master struct\n"); | ||
| 420 | master = spi_alloc_master(&op->dev, sizeof *ms); | ||
| 421 | if (!master) { | ||
| 422 | rc = -ENOMEM; | ||
| 423 | goto err_alloc; | ||
| 424 | } | ||
| 425 | master->bus_num = -1; | ||
| 426 | master->num_chipselect = 1; | ||
| 427 | master->setup = mpc52xx_spi_setup; | ||
| 428 | master->transfer = mpc52xx_spi_transfer; | ||
| 429 | dev_set_drvdata(&op->dev, master); | ||
| 430 | |||
| 431 | ms = spi_master_get_devdata(master); | ||
| 432 | ms->master = master; | ||
| 433 | ms->regs = regs; | ||
| 434 | ms->irq0 = irq_of_parse_and_map(op->node, 0); | ||
| 435 | ms->irq1 = irq_of_parse_and_map(op->node, 1); | ||
| 436 | ms->state = mpc52xx_spi_fsmstate_idle; | ||
| 437 | ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node); | ||
| 438 | spin_lock_init(&ms->lock); | ||
| 439 | INIT_LIST_HEAD(&ms->queue); | ||
| 440 | INIT_WORK(&ms->work, mpc52xx_spi_wq); | ||
| 441 | |||
| 442 | /* Decide if interrupts can be used */ | ||
| 443 | if (ms->irq0 && ms->irq1) { | ||
| 444 | rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, | ||
| 445 | "mpc5200-spi-modf", ms); | ||
| 446 | rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, | ||
| 447 | "mpc5200-spi-spiF", ms); | ||
| 448 | if (rc) { | ||
| 449 | free_irq(ms->irq0, ms); | ||
| 450 | free_irq(ms->irq1, ms); | ||
| 451 | ms->irq0 = ms->irq1 = 0; | ||
| 452 | } | ||
| 453 | } else { | ||
| 454 | /* operate in polled mode */ | ||
| 455 | ms->irq0 = ms->irq1 = 0; | ||
| 456 | } | ||
| 457 | |||
| 458 | if (!ms->irq0) | ||
| 459 | dev_info(&op->dev, "using polled mode\n"); | ||
| 460 | |||
| 461 | dev_dbg(&op->dev, "registering spi_master struct\n"); | ||
| 462 | rc = spi_register_master(master); | ||
| 463 | if (rc) | ||
| 464 | goto err_register; | ||
| 465 | |||
| 466 | of_register_spi_devices(master, op->node); | ||
| 467 | dev_info(&ms->master->dev, "registered MPC5200 SPI bus\n"); | ||
| 468 | |||
| 469 | return rc; | ||
| 470 | |||
| 471 | err_register: | ||
| 472 | dev_err(&ms->master->dev, "initialization failed\n"); | ||
| 473 | spi_master_put(master); | ||
| 474 | err_alloc: | ||
| 475 | err_init: | ||
| 476 | iounmap(regs); | ||
| 477 | return rc; | ||
| 478 | } | ||
| 479 | |||
| 480 | static int __devexit mpc52xx_spi_remove(struct of_device *op) | ||
| 481 | { | ||
| 482 | struct spi_master *master = dev_get_drvdata(&op->dev); | ||
| 483 | struct mpc52xx_spi *ms = spi_master_get_devdata(master); | ||
| 484 | |||
| 485 | free_irq(ms->irq0, ms); | ||
| 486 | free_irq(ms->irq1, ms); | ||
| 487 | |||
| 488 | spi_unregister_master(master); | ||
| 489 | spi_master_put(master); | ||
| 490 | iounmap(ms->regs); | ||
| 491 | |||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | static struct of_device_id mpc52xx_spi_match[] __devinitdata = { | ||
| 496 | { .compatible = "fsl,mpc5200-spi", }, | ||
| 497 | {} | ||
| 498 | }; | ||
| 499 | MODULE_DEVICE_TABLE(of, mpc52xx_spi_match); | ||
| 500 | |||
| 501 | static struct of_platform_driver mpc52xx_spi_of_driver = { | ||
| 502 | .owner = THIS_MODULE, | ||
| 503 | .name = "mpc52xx-spi", | ||
| 504 | .match_table = mpc52xx_spi_match, | ||
| 505 | .probe = mpc52xx_spi_probe, | ||
| 506 | .remove = __exit_p(mpc52xx_spi_remove), | ||
| 507 | }; | ||
| 508 | |||
| 509 | static int __init mpc52xx_spi_init(void) | ||
| 510 | { | ||
| 511 | return of_register_platform_driver(&mpc52xx_spi_of_driver); | ||
| 512 | } | ||
| 513 | module_init(mpc52xx_spi_init); | ||
| 514 | |||
| 515 | static void __exit mpc52xx_spi_exit(void) | ||
| 516 | { | ||
| 517 | of_unregister_platform_driver(&mpc52xx_spi_of_driver); | ||
| 518 | } | ||
| 519 | module_exit(mpc52xx_spi_exit); | ||
| 520 | |||
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 46b8c5c2f45e..5a143b9f6361 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, | |||
| 148 | { | 148 | { |
| 149 | u8 bits_per_word; | 149 | u8 bits_per_word; |
| 150 | 150 | ||
| 151 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | 151 | bits_per_word = (t && t->bits_per_word) |
| 152 | ? t->bits_per_word : spi->bits_per_word; | ||
| 152 | if (bits_per_word != 8) { | 153 | if (bits_per_word != 8) { |
| 153 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | 154 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
| 154 | __func__, bits_per_word); | 155 | __func__, bits_per_word); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3711b888d482..d958b76430a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -861,8 +861,10 @@ config GEF_WDT | |||
| 861 | Watchdog timer found in a number of GE Fanuc single board computers. | 861 | Watchdog timer found in a number of GE Fanuc single board computers. |
| 862 | 862 | ||
| 863 | config MPC5200_WDT | 863 | config MPC5200_WDT |
| 864 | tristate "MPC5200 Watchdog Timer" | 864 | bool "MPC52xx Watchdog Timer" |
| 865 | depends on PPC_MPC52xx | 865 | depends on PPC_MPC52xx |
| 866 | help | ||
| 867 | Use General Purpose Timer (GPT) 0 on the MPC5200 as Watchdog. | ||
| 866 | 868 | ||
| 867 | config 8xxx_WDT | 869 | config 8xxx_WDT |
| 868 | tristate "MPC8xxx Platform Watchdog Timer" | 870 | tristate "MPC8xxx Platform Watchdog Timer" |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 699199b1baa6..89c045dc468e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
| @@ -118,7 +118,6 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o | |||
| 118 | 118 | ||
| 119 | # POWERPC Architecture | 119 | # POWERPC Architecture |
| 120 | obj-$(CONFIG_GEF_WDT) += gef_wdt.o | 120 | obj-$(CONFIG_GEF_WDT) += gef_wdt.o |
| 121 | obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o | ||
| 122 | obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o | 121 | obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o |
| 123 | obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o | 122 | obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o |
| 124 | obj-$(CONFIG_PIKA_WDT) += pika_wdt.o | 123 | 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 fa9c47ce0ae7..000000000000 --- a/drivers/watchdog/mpc5200_wdt.c +++ /dev/null | |||
| @@ -1,293 +0,0 @@ | |||
| 1 | #include <linux/init.h> | ||
| 2 | #include <linux/module.h> | ||
| 3 | #include <linux/miscdevice.h> | ||
| 4 | #include <linux/watchdog.h> | ||
| 5 | #include <linux/io.h> | ||
| 6 | #include <linux/spinlock.h> | ||
| 7 | #include <linux/of_platform.h> | ||
| 8 | #include <linux/uaccess.h> | ||
| 9 | #include <asm/mpc52xx.h> | ||
| 10 | |||
| 11 | |||
| 12 | #define GPT_MODE_WDT (1 << 15) | ||
| 13 | #define GPT_MODE_CE (1 << 12) | ||
| 14 | #define GPT_MODE_MS_TIMER (0x4) | ||
| 15 | |||
| 16 | |||
| 17 | struct mpc5200_wdt { | ||
| 18 | unsigned count; /* timer ticks before watchdog kicks in */ | ||
| 19 | long ipb_freq; | ||
| 20 | struct miscdevice miscdev; | ||
| 21 | struct resource mem; | ||
| 22 | struct mpc52xx_gpt __iomem *regs; | ||
| 23 | spinlock_t io_lock; | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* is_active stores wether or not the /dev/watchdog device is opened */ | ||
| 27 | static unsigned long is_active; | ||
| 28 | |||
| 29 | /* misc devices don't provide a way, to get back to 'dev' or 'miscdev' from | ||
| 30 | * file operations, which sucks. But there can be max 1 watchdog anyway, so... | ||
| 31 | */ | ||
| 32 | static struct mpc5200_wdt *wdt_global; | ||
| 33 | |||
| 34 | |||
| 35 | /* helper to calculate timeout in timer counts */ | ||
| 36 | static void mpc5200_wdt_set_timeout(struct mpc5200_wdt *wdt, int timeout) | ||
| 37 | { | ||
| 38 | /* use biggest prescaler of 64k */ | ||
| 39 | wdt->count = (wdt->ipb_freq + 0xffff) / 0x10000 * timeout; | ||
| 40 | |||
| 41 | if (wdt->count > 0xffff) | ||
| 42 | wdt->count = 0xffff; | ||
| 43 | } | ||
| 44 | /* return timeout in seconds (calculated from timer count) */ | ||
| 45 | static int mpc5200_wdt_get_timeout(struct mpc5200_wdt *wdt) | ||
| 46 | { | ||
| 47 | return wdt->count * 0x10000 / wdt->ipb_freq; | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | /* watchdog operations */ | ||
| 52 | static int mpc5200_wdt_start(struct mpc5200_wdt *wdt) | ||
| 53 | { | ||
| 54 | spin_lock(&wdt->io_lock); | ||
| 55 | /* disable */ | ||
| 56 | out_be32(&wdt->regs->mode, 0); | ||
| 57 | /* set timeout, with maximum prescaler */ | ||
| 58 | out_be32(&wdt->regs->count, 0x0 | wdt->count); | ||
| 59 | /* enable watchdog */ | ||
| 60 | out_be32(&wdt->regs->mode, GPT_MODE_CE | GPT_MODE_WDT | | ||
| 61 | GPT_MODE_MS_TIMER); | ||
| 62 | spin_unlock(&wdt->io_lock); | ||
| 63 | |||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | static int mpc5200_wdt_ping(struct mpc5200_wdt *wdt) | ||
| 67 | { | ||
| 68 | spin_lock(&wdt->io_lock); | ||
| 69 | /* writing A5 to OCPW resets the watchdog */ | ||
| 70 | out_be32(&wdt->regs->mode, 0xA5000000 | | ||
| 71 | (0xffffff & in_be32(&wdt->regs->mode))); | ||
| 72 | spin_unlock(&wdt->io_lock); | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | static int mpc5200_wdt_stop(struct mpc5200_wdt *wdt) | ||
| 76 | { | ||
| 77 | spin_lock(&wdt->io_lock); | ||
| 78 | /* disable */ | ||
| 79 | out_be32(&wdt->regs->mode, 0); | ||
| 80 | spin_unlock(&wdt->io_lock); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | /* file operations */ | ||
| 86 | static ssize_t mpc5200_wdt_write(struct file *file, const char __user *data, | ||
| 87 | size_t len, loff_t *ppos) | ||
| 88 | { | ||
| 89 | struct mpc5200_wdt *wdt = file->private_data; | ||
| 90 | mpc5200_wdt_ping(wdt); | ||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | static struct watchdog_info mpc5200_wdt_info = { | ||
| 94 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
| 95 | .identity = "mpc5200 watchdog on GPT0", | ||
| 96 | }; | ||
| 97 | static long mpc5200_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 98 | unsigned long arg) | ||
| 99 | { | ||
| 100 | struct mpc5200_wdt *wdt = file->private_data; | ||
| 101 | int __user *data = (int __user *)arg; | ||
| 102 | int timeout; | ||
| 103 | int ret = 0; | ||
| 104 | |||
| 105 | switch (cmd) { | ||
| 106 | case WDIOC_GETSUPPORT: | ||
| 107 | ret = copy_to_user(data, &mpc5200_wdt_info, | ||
| 108 | sizeof(mpc5200_wdt_info)); | ||
| 109 | if (ret) | ||
| 110 | ret = -EFAULT; | ||
| 111 | break; | ||
| 112 | |||
| 113 | case WDIOC_GETSTATUS: | ||
| 114 | case WDIOC_GETBOOTSTATUS: | ||
| 115 | ret = put_user(0, data); | ||
| 116 | break; | ||
| 117 | |||
| 118 | case WDIOC_KEEPALIVE: | ||
| 119 | mpc5200_wdt_ping(wdt); | ||
| 120 | break; | ||
| 121 | |||
| 122 | case WDIOC_SETTIMEOUT: | ||
| 123 | ret = get_user(timeout, data); | ||
| 124 | if (ret) | ||
| 125 | break; | ||
| 126 | mpc5200_wdt_set_timeout(wdt, timeout); | ||
| 127 | mpc5200_wdt_start(wdt); | ||
| 128 | /* fall through and return the timeout */ | ||
| 129 | |||
| 130 | case WDIOC_GETTIMEOUT: | ||
| 131 | timeout = mpc5200_wdt_get_timeout(wdt); | ||
| 132 | ret = put_user(timeout, data); | ||
| 133 | break; | ||
| 134 | |||
| 135 | default: | ||
| 136 | ret = -ENOTTY; | ||
| 137 | } | ||
| 138 | return ret; | ||
| 139 | } | ||
| 140 | |||
| 141 | static int mpc5200_wdt_open(struct inode *inode, struct file *file) | ||
| 142 | { | ||
| 143 | /* /dev/watchdog can only be opened once */ | ||
| 144 | if (test_and_set_bit(0, &is_active)) | ||
| 145 | return -EBUSY; | ||
| 146 | |||
| 147 | /* Set and activate the watchdog */ | ||
| 148 | mpc5200_wdt_set_timeout(wdt_global, 30); | ||
| 149 | mpc5200_wdt_start(wdt_global); | ||
| 150 | file->private_data = wdt_global; | ||
| 151 | return nonseekable_open(inode, file); | ||
| 152 | } | ||
| 153 | static int mpc5200_wdt_release(struct inode *inode, struct file *file) | ||
| 154 | { | ||
| 155 | #if WATCHDOG_NOWAYOUT == 0 | ||
| 156 | struct mpc5200_wdt *wdt = file->private_data; | ||
| 157 | mpc5200_wdt_stop(wdt); | ||
| 158 | wdt->count = 0; /* == disabled */ | ||
| 159 | #endif | ||
| 160 | clear_bit(0, &is_active); | ||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | static const struct file_operations mpc5200_wdt_fops = { | ||
| 165 | .owner = THIS_MODULE, | ||
| 166 | .write = mpc5200_wdt_write, | ||
| 167 | .unlocked_ioctl = mpc5200_wdt_ioctl, | ||
| 168 | .open = mpc5200_wdt_open, | ||
| 169 | .release = mpc5200_wdt_release, | ||
| 170 | }; | ||
| 171 | |||
| 172 | /* module operations */ | ||
| 173 | static int mpc5200_wdt_probe(struct of_device *op, | ||
| 174 | const struct of_device_id *match) | ||
| 175 | { | ||
| 176 | struct mpc5200_wdt *wdt; | ||
| 177 | int err; | ||
| 178 | const void *has_wdt; | ||
| 179 | int size; | ||
| 180 | |||
| 181 | has_wdt = of_get_property(op->node, "has-wdt", NULL); | ||
| 182 | if (!has_wdt) | ||
| 183 | has_wdt = of_get_property(op->node, "fsl,has-wdt", NULL); | ||
| 184 | if (!has_wdt) | ||
| 185 | return -ENODEV; | ||
| 186 | |||
| 187 | wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); | ||
| 188 | if (!wdt) | ||
| 189 | return -ENOMEM; | ||
| 190 | |||
| 191 | wdt->ipb_freq = mpc5xxx_get_bus_frequency(op->node); | ||
| 192 | |||
| 193 | err = of_address_to_resource(op->node, 0, &wdt->mem); | ||
| 194 | if (err) | ||
| 195 | goto out_free; | ||
| 196 | size = wdt->mem.end - wdt->mem.start + 1; | ||
| 197 | if (!request_mem_region(wdt->mem.start, size, "mpc5200_wdt")) { | ||
| 198 | err = -ENODEV; | ||
| 199 | goto out_free; | ||
| 200 | } | ||
| 201 | wdt->regs = ioremap(wdt->mem.start, size); | ||
| 202 | if (!wdt->regs) { | ||
| 203 | err = -ENODEV; | ||
| 204 | goto out_release; | ||
| 205 | } | ||
| 206 | |||
| 207 | dev_set_drvdata(&op->dev, wdt); | ||
| 208 | spin_lock_init(&wdt->io_lock); | ||
| 209 | |||
| 210 | wdt->miscdev = (struct miscdevice) { | ||
| 211 | .minor = WATCHDOG_MINOR, | ||
| 212 | .name = "watchdog", | ||
| 213 | .fops = &mpc5200_wdt_fops, | ||
| 214 | .parent = &op->dev, | ||
| 215 | }; | ||
| 216 | wdt_global = wdt; | ||
| 217 | err = misc_register(&wdt->miscdev); | ||
| 218 | if (!err) | ||
| 219 | return 0; | ||
| 220 | |||
| 221 | iounmap(wdt->regs); | ||
| 222 | out_release: | ||
| 223 | release_mem_region(wdt->mem.start, size); | ||
| 224 | out_free: | ||
| 225 | kfree(wdt); | ||
| 226 | return err; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int mpc5200_wdt_remove(struct of_device *op) | ||
| 230 | { | ||
| 231 | struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); | ||
| 232 | |||
| 233 | mpc5200_wdt_stop(wdt); | ||
| 234 | misc_deregister(&wdt->miscdev); | ||
| 235 | iounmap(wdt->regs); | ||
| 236 | release_mem_region(wdt->mem.start, wdt->mem.end - wdt->mem.start + 1); | ||
| 237 | kfree(wdt); | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | static int mpc5200_wdt_suspend(struct of_device *op, pm_message_t state) | ||
| 242 | { | ||
| 243 | struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); | ||
| 244 | mpc5200_wdt_stop(wdt); | ||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | static int mpc5200_wdt_resume(struct of_device *op) | ||
| 248 | { | ||
| 249 | struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); | ||
| 250 | if (wdt->count) | ||
| 251 | mpc5200_wdt_start(wdt); | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | static int mpc5200_wdt_shutdown(struct of_device *op) | ||
| 255 | { | ||
| 256 | struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); | ||
| 257 | mpc5200_wdt_stop(wdt); | ||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | static struct of_device_id mpc5200_wdt_match[] = { | ||
| 262 | { .compatible = "mpc5200-gpt", }, | ||
| 263 | { .compatible = "fsl,mpc5200-gpt", }, | ||
| 264 | {}, | ||
| 265 | }; | ||
| 266 | static struct of_platform_driver mpc5200_wdt_driver = { | ||
| 267 | .owner = THIS_MODULE, | ||
| 268 | .name = "mpc5200-gpt-wdt", | ||
| 269 | .match_table = mpc5200_wdt_match, | ||
| 270 | .probe = mpc5200_wdt_probe, | ||
| 271 | .remove = mpc5200_wdt_remove, | ||
| 272 | .suspend = mpc5200_wdt_suspend, | ||
| 273 | .resume = mpc5200_wdt_resume, | ||
| 274 | .shutdown = mpc5200_wdt_shutdown, | ||
| 275 | }; | ||
| 276 | |||
| 277 | |||
| 278 | static int __init mpc5200_wdt_init(void) | ||
| 279 | { | ||
| 280 | return of_register_platform_driver(&mpc5200_wdt_driver); | ||
| 281 | } | ||
| 282 | |||
| 283 | static void __exit mpc5200_wdt_exit(void) | ||
| 284 | { | ||
| 285 | of_unregister_platform_driver(&mpc5200_wdt_driver); | ||
| 286 | } | ||
| 287 | |||
| 288 | module_init(mpc5200_wdt_init); | ||
| 289 | module_exit(mpc5200_wdt_exit); | ||
| 290 | |||
| 291 | MODULE_AUTHOR("Domen Puncer <domen.puncer@telargo.com>"); | ||
| 292 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| 293 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/include/linux/spi/mpc52xx_spi.h b/include/linux/spi/mpc52xx_spi.h new file mode 100644 index 000000000000..d1004cf09241 --- /dev/null +++ b/include/linux/spi/mpc52xx_spi.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | |||
| 2 | #ifndef INCLUDE_MPC5200_SPI_H | ||
| 3 | #define INCLUDE_MPC5200_SPI_H | ||
| 4 | |||
| 5 | extern void mpc52xx_spi_set_premessage_hook(struct spi_master *master, | ||
| 6 | void (*hook)(struct spi_message *m, | ||
| 7 | void *context), | ||
| 8 | void *hook_context); | ||
| 9 | |||
| 10 | #endif | ||
