xref: /kernel/linux/linux-6.6/arch/arm/common/scoop.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Support code for the SCOOP interface found on various Sharp PDAs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2004 Richard Purdie
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	Based on code written by Sharp/Lineo for 2.4 kernels
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <asm/hardware/scoop.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* PCMCIA to Scoop linkage
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci   There is no easy way to link multiple scoop devices into one
2262306a36Sopenharmony_ci   single entity for the pxa2xx_pcmcia device so this structure
2362306a36Sopenharmony_ci   is used which is setup by the platform code.
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci   This file is never modular so this symbol is always
2662306a36Sopenharmony_ci   accessile to the board support files.
2762306a36Sopenharmony_ci*/
2862306a36Sopenharmony_cistruct scoop_pcmcia_config *platform_scoop_config;
2962306a36Sopenharmony_ciEXPORT_SYMBOL(platform_scoop_config);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct  scoop_dev {
3262306a36Sopenharmony_ci	void __iomem *base;
3362306a36Sopenharmony_ci	struct gpio_chip gpio;
3462306a36Sopenharmony_ci	spinlock_t scoop_lock;
3562306a36Sopenharmony_ci	unsigned short suspend_clr;
3662306a36Sopenharmony_ci	unsigned short suspend_set;
3762306a36Sopenharmony_ci	u32 scoop_gpwr;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid reset_scoop(struct device *dev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct scoop_dev *sdev = dev_get_drvdata(dev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	iowrite16(0x0100, sdev->base + SCOOP_MCR);  /* 00 */
4562306a36Sopenharmony_ci	iowrite16(0x0000, sdev->base + SCOOP_CDR);  /* 04 */
4662306a36Sopenharmony_ci	iowrite16(0x0000, sdev->base + SCOOP_CCR);  /* 10 */
4762306a36Sopenharmony_ci	iowrite16(0x0000, sdev->base + SCOOP_IMR);  /* 18 */
4862306a36Sopenharmony_ci	iowrite16(0x00FF, sdev->base + SCOOP_IRM);  /* 14 */
4962306a36Sopenharmony_ci	iowrite16(0x0000, sdev->base + SCOOP_ISR);  /* 1C */
5062306a36Sopenharmony_ci	iowrite16(0x0000, sdev->base + SCOOP_IRM);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void __scoop_gpio_set(struct scoop_dev *sdev,
5462306a36Sopenharmony_ci			unsigned offset, int value)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned short gpwr;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	gpwr = ioread16(sdev->base + SCOOP_GPWR);
5962306a36Sopenharmony_ci	if (value)
6062306a36Sopenharmony_ci		gpwr |= 1 << (offset + 1);
6162306a36Sopenharmony_ci	else
6262306a36Sopenharmony_ci		gpwr &= ~(1 << (offset + 1));
6362306a36Sopenharmony_ci	iowrite16(gpwr, sdev->base + SCOOP_GPWR);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct scoop_dev *sdev = gpiochip_get_data(chip);
6962306a36Sopenharmony_ci	unsigned long flags;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->scoop_lock, flags);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	__scoop_gpio_set(sdev, offset, value);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct scoop_dev *sdev = gpiochip_get_data(chip);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* XXX: I'm unsure, but it seems so */
8362306a36Sopenharmony_ci	return !!(ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1)));
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int scoop_gpio_direction_input(struct gpio_chip *chip,
8762306a36Sopenharmony_ci			unsigned offset)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct scoop_dev *sdev = gpiochip_get_data(chip);
9062306a36Sopenharmony_ci	unsigned long flags;
9162306a36Sopenharmony_ci	unsigned short gpcr;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->scoop_lock, flags);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	gpcr = ioread16(sdev->base + SCOOP_GPCR);
9662306a36Sopenharmony_ci	gpcr &= ~(1 << (offset + 1));
9762306a36Sopenharmony_ci	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int scoop_gpio_direction_output(struct gpio_chip *chip,
10562306a36Sopenharmony_ci			unsigned offset, int value)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct scoop_dev *sdev = gpiochip_get_data(chip);
10862306a36Sopenharmony_ci	unsigned long flags;
10962306a36Sopenharmony_ci	unsigned short gpcr;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->scoop_lock, flags);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	__scoop_gpio_set(sdev, offset, value);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	gpcr = ioread16(sdev->base + SCOOP_GPCR);
11662306a36Sopenharmony_ci	gpcr |= 1 << (offset + 1);
11762306a36Sopenharmony_ci	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciunsigned short read_scoop_reg(struct device *dev, unsigned short reg)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct scoop_dev *sdev = dev_get_drvdata(dev);
12762306a36Sopenharmony_ci	return ioread16(sdev->base + reg);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_civoid write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct scoop_dev *sdev = dev_get_drvdata(dev);
13362306a36Sopenharmony_ci	iowrite16(data, sdev->base + reg);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciEXPORT_SYMBOL(reset_scoop);
13762306a36Sopenharmony_ciEXPORT_SYMBOL(read_scoop_reg);
13862306a36Sopenharmony_ciEXPORT_SYMBOL(write_scoop_reg);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#ifdef CONFIG_PM
14162306a36Sopenharmony_cistatic void check_scoop_reg(struct scoop_dev *sdev)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	unsigned short mcr;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	mcr = ioread16(sdev->base + SCOOP_MCR);
14662306a36Sopenharmony_ci	if ((mcr & 0x100) == 0)
14762306a36Sopenharmony_ci		iowrite16(0x0101, sdev->base + SCOOP_MCR);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int scoop_suspend(struct platform_device *dev, pm_message_t state)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct scoop_dev *sdev = platform_get_drvdata(dev);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	check_scoop_reg(sdev);
15562306a36Sopenharmony_ci	sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
15662306a36Sopenharmony_ci	iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int scoop_resume(struct platform_device *dev)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct scoop_dev *sdev = platform_get_drvdata(dev);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	check_scoop_reg(sdev);
16662306a36Sopenharmony_ci	iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci#else
17162306a36Sopenharmony_ci#define scoop_suspend	NULL
17262306a36Sopenharmony_ci#define scoop_resume	NULL
17362306a36Sopenharmony_ci#endif
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int scoop_probe(struct platform_device *pdev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct scoop_dev *devptr;
17862306a36Sopenharmony_ci	struct scoop_config *inf;
17962306a36Sopenharmony_ci	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!mem)
18362306a36Sopenharmony_ci		return -EINVAL;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
18662306a36Sopenharmony_ci	if (!devptr)
18762306a36Sopenharmony_ci		return -ENOMEM;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_lock_init(&devptr->scoop_lock);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	inf = pdev->dev.platform_data;
19262306a36Sopenharmony_ci	devptr->base = ioremap(mem->start, resource_size(mem));
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!devptr->base) {
19562306a36Sopenharmony_ci		ret = -ENOMEM;
19662306a36Sopenharmony_ci		goto err_ioremap;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	platform_set_drvdata(pdev, devptr);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	iowrite16(0x0140, devptr->base + SCOOP_MCR);
20462306a36Sopenharmony_ci	reset_scoop(&pdev->dev);
20562306a36Sopenharmony_ci	iowrite16(0x0000, devptr->base + SCOOP_CPR);
20662306a36Sopenharmony_ci	iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR);
20762306a36Sopenharmony_ci	iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	devptr->suspend_clr = inf->suspend_clr;
21062306a36Sopenharmony_ci	devptr->suspend_set = inf->suspend_set;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	devptr->gpio.base = -1;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (inf->gpio_base != 0) {
21562306a36Sopenharmony_ci		devptr->gpio.label = dev_name(&pdev->dev);
21662306a36Sopenharmony_ci		devptr->gpio.base = inf->gpio_base;
21762306a36Sopenharmony_ci		devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
21862306a36Sopenharmony_ci		devptr->gpio.set = scoop_gpio_set;
21962306a36Sopenharmony_ci		devptr->gpio.get = scoop_gpio_get;
22062306a36Sopenharmony_ci		devptr->gpio.direction_input = scoop_gpio_direction_input;
22162306a36Sopenharmony_ci		devptr->gpio.direction_output = scoop_gpio_direction_output;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		ret = gpiochip_add_data(&devptr->gpio, devptr);
22462306a36Sopenharmony_ci		if (ret)
22562306a36Sopenharmony_ci			goto err_gpio;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cierr_gpio:
23162306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
23262306a36Sopenharmony_cierr_ioremap:
23362306a36Sopenharmony_ci	iounmap(devptr->base);
23462306a36Sopenharmony_ci	kfree(devptr);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return ret;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void scoop_remove(struct platform_device *pdev)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct scoop_dev *sdev = platform_get_drvdata(pdev);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (sdev->gpio.base != -1)
24462306a36Sopenharmony_ci		gpiochip_remove(&sdev->gpio);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
24762306a36Sopenharmony_ci	iounmap(sdev->base);
24862306a36Sopenharmony_ci	kfree(sdev);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic struct platform_driver scoop_driver = {
25262306a36Sopenharmony_ci	.probe		= scoop_probe,
25362306a36Sopenharmony_ci	.remove_new	= scoop_remove,
25462306a36Sopenharmony_ci	.suspend	= scoop_suspend,
25562306a36Sopenharmony_ci	.resume		= scoop_resume,
25662306a36Sopenharmony_ci	.driver		= {
25762306a36Sopenharmony_ci		.name	= "sharp-scoop",
25862306a36Sopenharmony_ci	},
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int __init scoop_init(void)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	return platform_driver_register(&scoop_driver);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cisubsys_initcall(scoop_init);
267