blob: 314ebd3a1d718c60f2d7f16f2f8a5dcb98239681 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Support code for the SCOOP interface found on various Sharp PDAs
3 *
4 * Copyright (c) 2004 Richard Purdie
5 *
6 * Based on code written by Sharp/Lineo for 2.4 kernels
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/device.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080015#include <linux/string.h>
Tim Schmielaude259682006-01-08 01:02:05 -080016#include <linux/slab.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010017#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <asm/io.h>
19#include <asm/hardware/scoop.h>
20
Richard Purdie7ea3bbb2006-04-18 23:18:53 +010021/* PCMCIA to Scoop linkage
22
23 There is no easy way to link multiple scoop devices into one
24 single entity for the pxa2xx_pcmcia device so this structure
25 is used which is setup by the platform code.
26
27 This file is never modular so this symbol is always
28 accessile to the board support files.
29*/
30struct scoop_pcmcia_config *platform_scoop_config;
31EXPORT_SYMBOL(platform_scoop_config);
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035struct scoop_dev {
36 void *base;
37 spinlock_t scoop_lock;
Richard Purdie7c398982005-10-10 10:20:06 +010038 unsigned short suspend_clr;
39 unsigned short suspend_set;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 u32 scoop_gpwr;
41};
42
43void reset_scoop(struct device *dev)
44{
45 struct scoop_dev *sdev = dev_get_drvdata(dev);
46
47 SCOOP_REG(sdev->base,SCOOP_MCR) = 0x0100; // 00
48 SCOOP_REG(sdev->base,SCOOP_CDR) = 0x0000; // 04
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 SCOOP_REG(sdev->base,SCOOP_CCR) = 0x0000; // 10
50 SCOOP_REG(sdev->base,SCOOP_IMR) = 0x0000; // 18
51 SCOOP_REG(sdev->base,SCOOP_IRM) = 0x00FF; // 14
52 SCOOP_REG(sdev->base,SCOOP_ISR) = 0x0000; // 1C
53 SCOOP_REG(sdev->base,SCOOP_IRM) = 0x0000;
54}
55
56unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
57{
58 unsigned short gpio_bit;
59 unsigned long flag;
60 struct scoop_dev *sdev = dev_get_drvdata(dev);
61
62 spin_lock_irqsave(&sdev->scoop_lock, flag);
63 gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) | bit;
64 SCOOP_REG(sdev->base, SCOOP_GPWR) = gpio_bit;
65 spin_unlock_irqrestore(&sdev->scoop_lock, flag);
66
67 return gpio_bit;
68}
69
70unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
71{
72 unsigned short gpio_bit;
73 unsigned long flag;
74 struct scoop_dev *sdev = dev_get_drvdata(dev);
75
76 spin_lock_irqsave(&sdev->scoop_lock, flag);
77 gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) & ~bit;
78 SCOOP_REG(sdev->base,SCOOP_GPWR) = gpio_bit;
79 spin_unlock_irqrestore(&sdev->scoop_lock, flag);
80
81 return gpio_bit;
82}
83
84EXPORT_SYMBOL(set_scoop_gpio);
85EXPORT_SYMBOL(reset_scoop_gpio);
86
87unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
88{
89 struct scoop_dev *sdev = dev_get_drvdata(dev);
90 return SCOOP_REG(sdev->base,reg);
91}
92
93void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
94{
95 struct scoop_dev *sdev = dev_get_drvdata(dev);
96 SCOOP_REG(sdev->base,reg)=data;
97}
98
99EXPORT_SYMBOL(reset_scoop);
100EXPORT_SYMBOL(read_scoop_reg);
101EXPORT_SYMBOL(write_scoop_reg);
102
Richard Purdie7c398982005-10-10 10:20:06 +0100103static void check_scoop_reg(struct scoop_dev *sdev)
104{
105 unsigned short mcr;
106
107 mcr = SCOOP_REG(sdev->base, SCOOP_MCR);
108 if ((mcr & 0x100) == 0)
109 SCOOP_REG(sdev->base, SCOOP_MCR) = 0x0101;
110}
111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112#ifdef CONFIG_PM
Russell King3ae5eae2005-11-09 22:32:44 +0000113static int scoop_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Russell King3ae5eae2005-11-09 22:32:44 +0000115 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Russell King9480e302005-10-28 09:52:56 -0700117 check_scoop_reg(sdev);
118 sdev->scoop_gpwr = SCOOP_REG(sdev->base, SCOOP_GPWR);
119 SCOOP_REG(sdev->base, SCOOP_GPWR) = (sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set;
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 return 0;
122}
123
Russell King3ae5eae2005-11-09 22:32:44 +0000124static int scoop_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125{
Russell King3ae5eae2005-11-09 22:32:44 +0000126 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
Russell King9480e302005-10-28 09:52:56 -0700128 check_scoop_reg(sdev);
129 SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 return 0;
132}
133#else
134#define scoop_suspend NULL
135#define scoop_resume NULL
136#endif
137
Russell King3ae5eae2005-11-09 22:32:44 +0000138int __init scoop_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
140 struct scoop_dev *devptr;
141 struct scoop_config *inf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
143
144 if (!mem)
145 return -EINVAL;
146
Russell Kingd2a02b92006-03-20 19:46:41 +0000147 devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 if (!devptr)
Russell Kingd2a02b92006-03-20 19:46:41 +0000149 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 spin_lock_init(&devptr->scoop_lock);
152
Russell King3ae5eae2005-11-09 22:32:44 +0000153 inf = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
155
156 if (!devptr->base) {
157 kfree(devptr);
158 return -ENOMEM;
159 }
160
Russell King3ae5eae2005-11-09 22:32:44 +0000161 platform_set_drvdata(pdev, devptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
163 printk("Sharp Scoop Device found at 0x%08x -> 0x%08x\n",(unsigned int)mem->start,(unsigned int)devptr->base);
164
165 SCOOP_REG(devptr->base, SCOOP_MCR) = 0x0140;
Pavel Machekc35bf4a2005-11-12 20:25:25 +0000166 reset_scoop(&pdev->dev);
Richard Purdie945b9572006-01-05 20:44:57 +0000167 SCOOP_REG(devptr->base, SCOOP_CPR) = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
169 SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
170
Richard Purdie7c398982005-10-10 10:20:06 +0100171 devptr->suspend_clr = inf->suspend_clr;
172 devptr->suspend_set = inf->suspend_set;
173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return 0;
175}
176
Russell King3ae5eae2005-11-09 22:32:44 +0000177static int scoop_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178{
Russell King3ae5eae2005-11-09 22:32:44 +0000179 struct scoop_dev *sdev = platform_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if (sdev) {
181 iounmap(sdev->base);
182 kfree(sdev);
Russell King3ae5eae2005-11-09 22:32:44 +0000183 platform_set_drvdata(pdev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
185 return 0;
186}
187
Russell King3ae5eae2005-11-09 22:32:44 +0000188static struct platform_driver scoop_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 .probe = scoop_probe,
190 .remove = scoop_remove,
191 .suspend = scoop_suspend,
192 .resume = scoop_resume,
Russell King3ae5eae2005-11-09 22:32:44 +0000193 .driver = {
194 .name = "sharp-scoop",
195 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196};
197
198int __init scoop_init(void)
199{
Russell King3ae5eae2005-11-09 22:32:44 +0000200 return platform_driver_register(&scoop_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
203subsys_initcall(scoop_init);