1/*
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>
15#include <linux/string.h>
16#include <linux/slab.h>
17#include <linux/platform_device.h>
18#include <asm/io.h>
19#include <asm/hardware/scoop.h>
20
21/* 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
33#define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
34
35struct  scoop_dev {
36	void  *base;
37	spinlock_t scoop_lock;
38	unsigned short suspend_clr;
39	unsigned short suspend_set;
40	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
49	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
103static 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
112#ifdef CONFIG_PM
113static int scoop_suspend(struct platform_device *dev, pm_message_t state)
114{
115	struct scoop_dev *sdev = platform_get_drvdata(dev);
116
117	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
121	return 0;
122}
123
124static int scoop_resume(struct platform_device *dev)
125{
126	struct scoop_dev *sdev = platform_get_drvdata(dev);
127
128	check_scoop_reg(sdev);
129	SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
130
131	return 0;
132}
133#else
134#define scoop_suspend	NULL
135#define scoop_resume	NULL
136#endif
137
138int __init scoop_probe(struct platform_device *pdev)
139{
140	struct scoop_dev *devptr;
141	struct scoop_config *inf;
142	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
143
144	if (!mem)
145		return -EINVAL;
146
147	devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
148	if (!devptr)
149		return -ENOMEM;
150
151	spin_lock_init(&devptr->scoop_lock);
152
153	inf = pdev->dev.platform_data;
154	devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
155
156	if (!devptr->base) {
157		kfree(devptr);
158		return -ENOMEM;
159	}
160
161	platform_set_drvdata(pdev, devptr);
162
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;
166	reset_scoop(&pdev->dev);
167	SCOOP_REG(devptr->base, SCOOP_CPR) = 0x0000;
168	SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
169	SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
170
171	devptr->suspend_clr = inf->suspend_clr;
172	devptr->suspend_set = inf->suspend_set;
173
174	return 0;
175}
176
177static int scoop_remove(struct platform_device *pdev)
178{
179	struct scoop_dev *sdev = platform_get_drvdata(pdev);
180	if (sdev) {
181		iounmap(sdev->base);
182		kfree(sdev);
183		platform_set_drvdata(pdev, NULL);
184	}
185	return 0;
186}
187
188static struct platform_driver scoop_driver = {
189	.probe		= scoop_probe,
190	.remove 	= scoop_remove,
191	.suspend	= scoop_suspend,
192	.resume		= scoop_resume,
193	.driver		= {
194		.name	= "sharp-scoop",
195	},
196};
197
198int __init scoop_init(void)
199{
200	return platform_driver_register(&scoop_driver);
201}
202
203subsys_initcall(scoop_init);
204