162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/arch/arm/common/sa1111.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * SA1111 support
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Original code by John Dorsey
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file contains all generic SA1111 support.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * All initialization functions provided here are intended to be called
1262306a36Sopenharmony_ci * from machine specific code with proper arguments when required.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/errno.h>
2162306a36Sopenharmony_ci#include <linux/ioport.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/spinlock.h>
2562306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
2662306a36Sopenharmony_ci#include <linux/clk.h>
2762306a36Sopenharmony_ci#include <linux/io.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <asm/mach/irq.h>
3062306a36Sopenharmony_ci#include <asm/mach-types.h>
3162306a36Sopenharmony_ci#include <linux/sizes.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <asm/hardware/sa1111.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_SA1100
3662306a36Sopenharmony_ci#include <mach/hardware.h>
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* SA1111 IRQs */
4062306a36Sopenharmony_ci#define IRQ_GPAIN0		(0)
4162306a36Sopenharmony_ci#define IRQ_GPAIN1		(1)
4262306a36Sopenharmony_ci#define IRQ_GPAIN2		(2)
4362306a36Sopenharmony_ci#define IRQ_GPAIN3		(3)
4462306a36Sopenharmony_ci#define IRQ_GPBIN0		(4)
4562306a36Sopenharmony_ci#define IRQ_GPBIN1		(5)
4662306a36Sopenharmony_ci#define IRQ_GPBIN2		(6)
4762306a36Sopenharmony_ci#define IRQ_GPBIN3		(7)
4862306a36Sopenharmony_ci#define IRQ_GPBIN4		(8)
4962306a36Sopenharmony_ci#define IRQ_GPBIN5		(9)
5062306a36Sopenharmony_ci#define IRQ_GPCIN0		(10)
5162306a36Sopenharmony_ci#define IRQ_GPCIN1		(11)
5262306a36Sopenharmony_ci#define IRQ_GPCIN2		(12)
5362306a36Sopenharmony_ci#define IRQ_GPCIN3		(13)
5462306a36Sopenharmony_ci#define IRQ_GPCIN4		(14)
5562306a36Sopenharmony_ci#define IRQ_GPCIN5		(15)
5662306a36Sopenharmony_ci#define IRQ_GPCIN6		(16)
5762306a36Sopenharmony_ci#define IRQ_GPCIN7		(17)
5862306a36Sopenharmony_ci#define IRQ_MSTXINT		(18)
5962306a36Sopenharmony_ci#define IRQ_MSRXINT		(19)
6062306a36Sopenharmony_ci#define IRQ_MSSTOPERRINT	(20)
6162306a36Sopenharmony_ci#define IRQ_TPTXINT		(21)
6262306a36Sopenharmony_ci#define IRQ_TPRXINT		(22)
6362306a36Sopenharmony_ci#define IRQ_TPSTOPERRINT	(23)
6462306a36Sopenharmony_ci#define SSPXMTINT		(24)
6562306a36Sopenharmony_ci#define SSPRCVINT		(25)
6662306a36Sopenharmony_ci#define SSPROR			(26)
6762306a36Sopenharmony_ci#define AUDXMTDMADONEA		(32)
6862306a36Sopenharmony_ci#define AUDRCVDMADONEA		(33)
6962306a36Sopenharmony_ci#define AUDXMTDMADONEB		(34)
7062306a36Sopenharmony_ci#define AUDRCVDMADONEB		(35)
7162306a36Sopenharmony_ci#define AUDTFSR			(36)
7262306a36Sopenharmony_ci#define AUDRFSR			(37)
7362306a36Sopenharmony_ci#define AUDTUR			(38)
7462306a36Sopenharmony_ci#define AUDROR			(39)
7562306a36Sopenharmony_ci#define AUDDTS			(40)
7662306a36Sopenharmony_ci#define AUDRDD			(41)
7762306a36Sopenharmony_ci#define AUDSTO			(42)
7862306a36Sopenharmony_ci#define IRQ_USBPWR		(43)
7962306a36Sopenharmony_ci#define IRQ_HCIM		(44)
8062306a36Sopenharmony_ci#define IRQ_HCIBUFFACC		(45)
8162306a36Sopenharmony_ci#define IRQ_HCIRMTWKP		(46)
8262306a36Sopenharmony_ci#define IRQ_NHCIMFCIR		(47)
8362306a36Sopenharmony_ci#define IRQ_USB_PORT_RESUME	(48)
8462306a36Sopenharmony_ci#define IRQ_S0_READY_NINT	(49)
8562306a36Sopenharmony_ci#define IRQ_S1_READY_NINT	(50)
8662306a36Sopenharmony_ci#define IRQ_S0_CD_VALID		(51)
8762306a36Sopenharmony_ci#define IRQ_S1_CD_VALID		(52)
8862306a36Sopenharmony_ci#define IRQ_S0_BVD1_STSCHG	(53)
8962306a36Sopenharmony_ci#define IRQ_S1_BVD1_STSCHG	(54)
9062306a36Sopenharmony_ci#define SA1111_IRQ_NR		(55)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciextern void sa1110_mb_enable(void);
9362306a36Sopenharmony_ciextern void sa1110_mb_disable(void);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * We keep the following data for the overall SA1111.  Note that the
9762306a36Sopenharmony_ci * struct device and struct resource are "fake"; they should be supplied
9862306a36Sopenharmony_ci * by the bus above us.  However, in the interests of getting all SA1111
9962306a36Sopenharmony_ci * drivers converted over to the device model, we provide this as an
10062306a36Sopenharmony_ci * anchor point for all the other drivers.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistruct sa1111 {
10362306a36Sopenharmony_ci	struct device	*dev;
10462306a36Sopenharmony_ci	struct clk	*clk;
10562306a36Sopenharmony_ci	unsigned long	phys;
10662306a36Sopenharmony_ci	int		irq;
10762306a36Sopenharmony_ci	int		irq_base;	/* base for cascaded on-chip IRQs */
10862306a36Sopenharmony_ci	spinlock_t	lock;
10962306a36Sopenharmony_ci	void __iomem	*base;
11062306a36Sopenharmony_ci	struct sa1111_platform_data *pdata;
11162306a36Sopenharmony_ci	struct irq_domain *irqdomain;
11262306a36Sopenharmony_ci	struct gpio_chip gc;
11362306a36Sopenharmony_ci#ifdef CONFIG_PM
11462306a36Sopenharmony_ci	void		*saved_state;
11562306a36Sopenharmony_ci#endif
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * We _really_ need to eliminate this.  Its only users
12062306a36Sopenharmony_ci * are the PWM and DMA checking code.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic struct sa1111 *g_sa1111;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct sa1111_dev_info {
12562306a36Sopenharmony_ci	unsigned long	offset;
12662306a36Sopenharmony_ci	unsigned long	skpcr_mask;
12762306a36Sopenharmony_ci	bool		dma;
12862306a36Sopenharmony_ci	unsigned int	devid;
12962306a36Sopenharmony_ci	unsigned int	hwirq[6];
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct sa1111_dev_info sa1111_devices[] = {
13362306a36Sopenharmony_ci	{
13462306a36Sopenharmony_ci		.offset		= SA1111_USB,
13562306a36Sopenharmony_ci		.skpcr_mask	= SKPCR_UCLKEN,
13662306a36Sopenharmony_ci		.dma		= true,
13762306a36Sopenharmony_ci		.devid		= SA1111_DEVID_USB,
13862306a36Sopenharmony_ci		.hwirq = {
13962306a36Sopenharmony_ci			IRQ_USBPWR,
14062306a36Sopenharmony_ci			IRQ_HCIM,
14162306a36Sopenharmony_ci			IRQ_HCIBUFFACC,
14262306a36Sopenharmony_ci			IRQ_HCIRMTWKP,
14362306a36Sopenharmony_ci			IRQ_NHCIMFCIR,
14462306a36Sopenharmony_ci			IRQ_USB_PORT_RESUME
14562306a36Sopenharmony_ci		},
14662306a36Sopenharmony_ci	},
14762306a36Sopenharmony_ci	{
14862306a36Sopenharmony_ci		.offset		= 0x0600,
14962306a36Sopenharmony_ci		.skpcr_mask	= SKPCR_I2SCLKEN | SKPCR_L3CLKEN,
15062306a36Sopenharmony_ci		.dma		= true,
15162306a36Sopenharmony_ci		.devid		= SA1111_DEVID_SAC,
15262306a36Sopenharmony_ci		.hwirq = {
15362306a36Sopenharmony_ci			AUDXMTDMADONEA,
15462306a36Sopenharmony_ci			AUDXMTDMADONEB,
15562306a36Sopenharmony_ci			AUDRCVDMADONEA,
15662306a36Sopenharmony_ci			AUDRCVDMADONEB
15762306a36Sopenharmony_ci		},
15862306a36Sopenharmony_ci	},
15962306a36Sopenharmony_ci	{
16062306a36Sopenharmony_ci		.offset		= 0x0800,
16162306a36Sopenharmony_ci		.skpcr_mask	= SKPCR_SCLKEN,
16262306a36Sopenharmony_ci		.devid		= SA1111_DEVID_SSP,
16362306a36Sopenharmony_ci	},
16462306a36Sopenharmony_ci	{
16562306a36Sopenharmony_ci		.offset		= SA1111_KBD,
16662306a36Sopenharmony_ci		.skpcr_mask	= SKPCR_PTCLKEN,
16762306a36Sopenharmony_ci		.devid		= SA1111_DEVID_PS2_KBD,
16862306a36Sopenharmony_ci		.hwirq = {
16962306a36Sopenharmony_ci			IRQ_TPRXINT,
17062306a36Sopenharmony_ci			IRQ_TPTXINT
17162306a36Sopenharmony_ci		},
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	{
17462306a36Sopenharmony_ci		.offset		= SA1111_MSE,
17562306a36Sopenharmony_ci		.skpcr_mask	= SKPCR_PMCLKEN,
17662306a36Sopenharmony_ci		.devid		= SA1111_DEVID_PS2_MSE,
17762306a36Sopenharmony_ci		.hwirq = {
17862306a36Sopenharmony_ci			IRQ_MSRXINT,
17962306a36Sopenharmony_ci			IRQ_MSTXINT
18062306a36Sopenharmony_ci		},
18162306a36Sopenharmony_ci	},
18262306a36Sopenharmony_ci	{
18362306a36Sopenharmony_ci		.offset		= 0x1800,
18462306a36Sopenharmony_ci		.skpcr_mask	= 0,
18562306a36Sopenharmony_ci		.devid		= SA1111_DEVID_PCMCIA,
18662306a36Sopenharmony_ci		.hwirq = {
18762306a36Sopenharmony_ci			IRQ_S0_READY_NINT,
18862306a36Sopenharmony_ci			IRQ_S0_CD_VALID,
18962306a36Sopenharmony_ci			IRQ_S0_BVD1_STSCHG,
19062306a36Sopenharmony_ci			IRQ_S1_READY_NINT,
19162306a36Sopenharmony_ci			IRQ_S1_CD_VALID,
19262306a36Sopenharmony_ci			IRQ_S1_BVD1_STSCHG,
19362306a36Sopenharmony_ci		},
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int sa1111_map_irq(struct sa1111 *sachip, irq_hw_number_t hwirq)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	return irq_create_mapping(sachip->irqdomain, hwirq);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * SA1111 interrupt support.  Since clearing an IRQ while there are
20462306a36Sopenharmony_ci * active IRQs causes the interrupt output to pulse, the upper levels
20562306a36Sopenharmony_ci * will call us again if there are more interrupts to process.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic void sa1111_irq_handler(struct irq_desc *desc)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	unsigned int stat0, stat1, i;
21062306a36Sopenharmony_ci	struct sa1111 *sachip = irq_desc_get_handler_data(desc);
21162306a36Sopenharmony_ci	struct irq_domain *irqdomain;
21262306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	stat0 = readl_relaxed(mapbase + SA1111_INTSTATCLR0);
21562306a36Sopenharmony_ci	stat1 = readl_relaxed(mapbase + SA1111_INTSTATCLR1);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	writel_relaxed(stat0, mapbase + SA1111_INTSTATCLR0);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	desc->irq_data.chip->irq_ack(&desc->irq_data);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	writel_relaxed(stat1, mapbase + SA1111_INTSTATCLR1);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (stat0 == 0 && stat1 == 0) {
22462306a36Sopenharmony_ci		do_bad_IRQ(desc);
22562306a36Sopenharmony_ci		return;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	irqdomain = sachip->irqdomain;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	for (i = 0; stat0; i++, stat0 >>= 1)
23162306a36Sopenharmony_ci		if (stat0 & 1)
23262306a36Sopenharmony_ci			generic_handle_domain_irq(irqdomain, i);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (i = 32; stat1; i++, stat1 >>= 1)
23562306a36Sopenharmony_ci		if (stat1 & 1)
23662306a36Sopenharmony_ci			generic_handle_domain_irq(irqdomain, i);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* For level-based interrupts */
23962306a36Sopenharmony_ci	desc->irq_data.chip->irq_unmask(&desc->irq_data);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic u32 sa1111_irqmask(struct irq_data *d)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	return BIT(irqd_to_hwirq(d) & 31);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int sa1111_irqbank(struct irq_data *d)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	return (irqd_to_hwirq(d) / 32) * 4;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void sa1111_ack_irq(struct irq_data *d)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void sa1111_mask_irq(struct irq_data *d)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
25962306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
26062306a36Sopenharmony_ci	u32 ie;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ie = readl_relaxed(mapbase + SA1111_INTEN0);
26362306a36Sopenharmony_ci	ie &= ~sa1111_irqmask(d);
26462306a36Sopenharmony_ci	writel(ie, mapbase + SA1111_INTEN0);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void sa1111_unmask_irq(struct irq_data *d)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
27062306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
27162306a36Sopenharmony_ci	u32 ie;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ie = readl_relaxed(mapbase + SA1111_INTEN0);
27462306a36Sopenharmony_ci	ie |= sa1111_irqmask(d);
27562306a36Sopenharmony_ci	writel_relaxed(ie, mapbase + SA1111_INTEN0);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/*
27962306a36Sopenharmony_ci * Attempt to re-trigger the interrupt.  The SA1111 contains a register
28062306a36Sopenharmony_ci * (INTSET) which claims to do this.  However, in practice no amount of
28162306a36Sopenharmony_ci * manipulation of INTEN and INTSET guarantees that the interrupt will
28262306a36Sopenharmony_ci * be triggered.  In fact, its very difficult, if not impossible to get
28362306a36Sopenharmony_ci * INTSET to re-trigger the interrupt.
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic int sa1111_retrigger_irq(struct irq_data *d)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
28862306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
28962306a36Sopenharmony_ci	u32 ip, mask = sa1111_irqmask(d);
29062306a36Sopenharmony_ci	int i;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ip = readl_relaxed(mapbase + SA1111_INTPOL0);
29362306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
29462306a36Sopenharmony_ci		writel_relaxed(ip ^ mask, mapbase + SA1111_INTPOL0);
29562306a36Sopenharmony_ci		writel_relaxed(ip, mapbase + SA1111_INTPOL0);
29662306a36Sopenharmony_ci		if (readl_relaxed(mapbase + SA1111_INTSTATCLR0) & mask)
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (i == 8) {
30162306a36Sopenharmony_ci		pr_err("Danger Will Robinson: failed to re-trigger IRQ%d\n",
30262306a36Sopenharmony_ci		       d->irq);
30362306a36Sopenharmony_ci		return 0;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return 1;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int sa1111_type_irq(struct irq_data *d, unsigned int flags)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
31262306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
31362306a36Sopenharmony_ci	u32 ip, mask = sa1111_irqmask(d);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (flags == IRQ_TYPE_PROBE)
31662306a36Sopenharmony_ci		return 0;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if ((!(flags & IRQ_TYPE_EDGE_RISING) ^ !(flags & IRQ_TYPE_EDGE_FALLING)) == 0)
31962306a36Sopenharmony_ci		return -EINVAL;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	ip = readl_relaxed(mapbase + SA1111_INTPOL0);
32262306a36Sopenharmony_ci	if (flags & IRQ_TYPE_EDGE_RISING)
32362306a36Sopenharmony_ci		ip &= ~mask;
32462306a36Sopenharmony_ci	else
32562306a36Sopenharmony_ci		ip |= mask;
32662306a36Sopenharmony_ci	writel_relaxed(ip, mapbase + SA1111_INTPOL0);
32762306a36Sopenharmony_ci	writel_relaxed(ip, mapbase + SA1111_WAKEPOL0);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int sa1111_wake_irq(struct irq_data *d, unsigned int on)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
33562306a36Sopenharmony_ci	void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
33662306a36Sopenharmony_ci	u32 we, mask = sa1111_irqmask(d);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	we = readl_relaxed(mapbase + SA1111_WAKEEN0);
33962306a36Sopenharmony_ci	if (on)
34062306a36Sopenharmony_ci		we |= mask;
34162306a36Sopenharmony_ci	else
34262306a36Sopenharmony_ci		we &= ~mask;
34362306a36Sopenharmony_ci	writel_relaxed(we, mapbase + SA1111_WAKEEN0);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct irq_chip sa1111_irq_chip = {
34962306a36Sopenharmony_ci	.name		= "SA1111",
35062306a36Sopenharmony_ci	.irq_ack	= sa1111_ack_irq,
35162306a36Sopenharmony_ci	.irq_mask	= sa1111_mask_irq,
35262306a36Sopenharmony_ci	.irq_unmask	= sa1111_unmask_irq,
35362306a36Sopenharmony_ci	.irq_retrigger	= sa1111_retrigger_irq,
35462306a36Sopenharmony_ci	.irq_set_type	= sa1111_type_irq,
35562306a36Sopenharmony_ci	.irq_set_wake	= sa1111_wake_irq,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int sa1111_irqdomain_map(struct irq_domain *d, unsigned int irq,
35962306a36Sopenharmony_ci	irq_hw_number_t hwirq)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct sa1111 *sachip = d->host_data;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* Disallow unavailable interrupts */
36462306a36Sopenharmony_ci	if (hwirq > SSPROR && hwirq < AUDXMTDMADONEA)
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	irq_set_chip_data(irq, sachip);
36862306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &sa1111_irq_chip, handle_edge_irq);
36962306a36Sopenharmony_ci	irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic const struct irq_domain_ops sa1111_irqdomain_ops = {
37562306a36Sopenharmony_ci	.map = sa1111_irqdomain_map,
37662306a36Sopenharmony_ci	.xlate = irq_domain_xlate_twocell,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	void __iomem *irqbase = sachip->base + SA1111_INTC;
38262306a36Sopenharmony_ci	int ret;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * We're guaranteed that this region hasn't been taken.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	request_mem_region(sachip->phys + SA1111_INTC, 512, "irq");
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ret = irq_alloc_descs(-1, irq_base, SA1111_IRQ_NR, -1);
39062306a36Sopenharmony_ci	if (ret <= 0) {
39162306a36Sopenharmony_ci		dev_err(sachip->dev, "unable to allocate %u irqs: %d\n",
39262306a36Sopenharmony_ci			SA1111_IRQ_NR, ret);
39362306a36Sopenharmony_ci		if (ret == 0)
39462306a36Sopenharmony_ci			ret = -EINVAL;
39562306a36Sopenharmony_ci		return ret;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	sachip->irq_base = ret;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* disable all IRQs */
40162306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_INTEN0);
40262306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_INTEN1);
40362306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_WAKEEN0);
40462306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_WAKEEN1);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/*
40762306a36Sopenharmony_ci	 * detect on rising edge.  Note: Feb 2001 Errata for SA1111
40862306a36Sopenharmony_ci	 * specifies that S0ReadyInt and S1ReadyInt should be '1'.
40962306a36Sopenharmony_ci	 */
41062306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_INTPOL0);
41162306a36Sopenharmony_ci	writel_relaxed(BIT(IRQ_S0_READY_NINT & 31) |
41262306a36Sopenharmony_ci		       BIT(IRQ_S1_READY_NINT & 31),
41362306a36Sopenharmony_ci		       irqbase + SA1111_INTPOL1);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* clear all IRQs */
41662306a36Sopenharmony_ci	writel_relaxed(~0, irqbase + SA1111_INTSTATCLR0);
41762306a36Sopenharmony_ci	writel_relaxed(~0, irqbase + SA1111_INTSTATCLR1);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	sachip->irqdomain = irq_domain_add_linear(NULL, SA1111_IRQ_NR,
42062306a36Sopenharmony_ci						  &sa1111_irqdomain_ops,
42162306a36Sopenharmony_ci						  sachip);
42262306a36Sopenharmony_ci	if (!sachip->irqdomain) {
42362306a36Sopenharmony_ci		irq_free_descs(sachip->irq_base, SA1111_IRQ_NR);
42462306a36Sopenharmony_ci		return -ENOMEM;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	irq_domain_associate_many(sachip->irqdomain,
42862306a36Sopenharmony_ci				  sachip->irq_base + IRQ_GPAIN0,
42962306a36Sopenharmony_ci				  IRQ_GPAIN0, SSPROR + 1 - IRQ_GPAIN0);
43062306a36Sopenharmony_ci	irq_domain_associate_many(sachip->irqdomain,
43162306a36Sopenharmony_ci				  sachip->irq_base + AUDXMTDMADONEA,
43262306a36Sopenharmony_ci				  AUDXMTDMADONEA,
43362306a36Sopenharmony_ci				  IRQ_S1_BVD1_STSCHG + 1 - AUDXMTDMADONEA);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/*
43662306a36Sopenharmony_ci	 * Register SA1111 interrupt
43762306a36Sopenharmony_ci	 */
43862306a36Sopenharmony_ci	irq_set_irq_type(sachip->irq, IRQ_TYPE_EDGE_RISING);
43962306a36Sopenharmony_ci	irq_set_chained_handler_and_data(sachip->irq, sa1111_irq_handler,
44062306a36Sopenharmony_ci					 sachip);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	dev_info(sachip->dev, "Providing IRQ%u-%u\n",
44362306a36Sopenharmony_ci		sachip->irq_base, sachip->irq_base + SA1111_IRQ_NR - 1);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void sa1111_remove_irq(struct sa1111 *sachip)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct irq_domain *domain = sachip->irqdomain;
45162306a36Sopenharmony_ci	void __iomem *irqbase = sachip->base + SA1111_INTC;
45262306a36Sopenharmony_ci	int i;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* disable all IRQs */
45562306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_INTEN0);
45662306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_INTEN1);
45762306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_WAKEEN0);
45862306a36Sopenharmony_ci	writel_relaxed(0, irqbase + SA1111_WAKEEN1);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	irq_set_chained_handler_and_data(sachip->irq, NULL, NULL);
46162306a36Sopenharmony_ci	for (i = 0; i < SA1111_IRQ_NR; i++)
46262306a36Sopenharmony_ci		irq_dispose_mapping(irq_find_mapping(domain, i));
46362306a36Sopenharmony_ci	irq_domain_remove(domain);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	release_mem_region(sachip->phys + SA1111_INTC, 512);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cienum {
46962306a36Sopenharmony_ci	SA1111_GPIO_PXDDR = (SA1111_GPIO_PADDR - SA1111_GPIO_PADDR),
47062306a36Sopenharmony_ci	SA1111_GPIO_PXDRR = (SA1111_GPIO_PADRR - SA1111_GPIO_PADDR),
47162306a36Sopenharmony_ci	SA1111_GPIO_PXDWR = (SA1111_GPIO_PADWR - SA1111_GPIO_PADDR),
47262306a36Sopenharmony_ci	SA1111_GPIO_PXSDR = (SA1111_GPIO_PASDR - SA1111_GPIO_PADDR),
47362306a36Sopenharmony_ci	SA1111_GPIO_PXSSR = (SA1111_GPIO_PASSR - SA1111_GPIO_PADDR),
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic struct sa1111 *gc_to_sa1111(struct gpio_chip *gc)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	return container_of(gc, struct sa1111, gc);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void __iomem *sa1111_gpio_map_reg(struct sa1111 *sachip, unsigned offset)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	void __iomem *reg = sachip->base + SA1111_GPIO;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (offset < 4)
48662306a36Sopenharmony_ci		return reg + SA1111_GPIO_PADDR;
48762306a36Sopenharmony_ci	if (offset < 10)
48862306a36Sopenharmony_ci		return reg + SA1111_GPIO_PBDDR;
48962306a36Sopenharmony_ci	if (offset < 18)
49062306a36Sopenharmony_ci		return reg + SA1111_GPIO_PCDDR;
49162306a36Sopenharmony_ci	return NULL;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic u32 sa1111_gpio_map_bit(unsigned offset)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	if (offset < 4)
49762306a36Sopenharmony_ci		return BIT(offset);
49862306a36Sopenharmony_ci	if (offset < 10)
49962306a36Sopenharmony_ci		return BIT(offset - 4);
50062306a36Sopenharmony_ci	if (offset < 18)
50162306a36Sopenharmony_ci		return BIT(offset - 10);
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void sa1111_gpio_modify(void __iomem *reg, u32 mask, u32 set)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	u32 val;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	val = readl_relaxed(reg);
51062306a36Sopenharmony_ci	val &= ~mask;
51162306a36Sopenharmony_ci	val |= mask & set;
51262306a36Sopenharmony_ci	writel_relaxed(val, reg);
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int sa1111_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
51862306a36Sopenharmony_ci	void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
51962306a36Sopenharmony_ci	u32 mask = sa1111_gpio_map_bit(offset);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return !!(readl_relaxed(reg + SA1111_GPIO_PXDDR) & mask);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int sa1111_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
52762306a36Sopenharmony_ci	unsigned long flags;
52862306a36Sopenharmony_ci	void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
52962306a36Sopenharmony_ci	u32 mask = sa1111_gpio_map_bit(offset);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
53262306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, mask);
53362306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, mask);
53462306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int sa1111_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
54062306a36Sopenharmony_ci	int value)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
54362306a36Sopenharmony_ci	unsigned long flags;
54462306a36Sopenharmony_ci	void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
54562306a36Sopenharmony_ci	u32 mask = sa1111_gpio_map_bit(offset);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
54862306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
54962306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
55062306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, 0);
55162306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, 0);
55262306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return 0;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int sa1111_gpio_get(struct gpio_chip *gc, unsigned offset)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
56062306a36Sopenharmony_ci	void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
56162306a36Sopenharmony_ci	u32 mask = sa1111_gpio_map_bit(offset);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return !!(readl_relaxed(reg + SA1111_GPIO_PXDRR) & mask);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void sa1111_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
56962306a36Sopenharmony_ci	unsigned long flags;
57062306a36Sopenharmony_ci	void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
57162306a36Sopenharmony_ci	u32 mask = sa1111_gpio_map_bit(offset);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
57462306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
57562306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
57662306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic void sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
58062306a36Sopenharmony_ci	unsigned long *bits)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
58362306a36Sopenharmony_ci	unsigned long flags;
58462306a36Sopenharmony_ci	void __iomem *reg = sachip->base + SA1111_GPIO;
58562306a36Sopenharmony_ci	u32 msk, val;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	msk = *mask;
58862306a36Sopenharmony_ci	val = *bits;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
59162306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PADWR, msk & 15, val);
59262306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PASSR, msk & 15, val);
59362306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PBDWR, (msk >> 4) & 255, val >> 4);
59462306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PBSSR, (msk >> 4) & 255, val >> 4);
59562306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PCDWR, (msk >> 12) & 255, val >> 12);
59662306a36Sopenharmony_ci	sa1111_gpio_modify(reg + SA1111_GPIO_PCSSR, (msk >> 12) & 255, val >> 12);
59762306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int sa1111_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct sa1111 *sachip = gc_to_sa1111(gc);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return sa1111_map_irq(sachip, offset);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int sa1111_setup_gpios(struct sa1111 *sachip)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	sachip->gc.label = "sa1111";
61062306a36Sopenharmony_ci	sachip->gc.parent = sachip->dev;
61162306a36Sopenharmony_ci	sachip->gc.owner = THIS_MODULE;
61262306a36Sopenharmony_ci	sachip->gc.get_direction = sa1111_gpio_get_direction;
61362306a36Sopenharmony_ci	sachip->gc.direction_input = sa1111_gpio_direction_input;
61462306a36Sopenharmony_ci	sachip->gc.direction_output = sa1111_gpio_direction_output;
61562306a36Sopenharmony_ci	sachip->gc.get = sa1111_gpio_get;
61662306a36Sopenharmony_ci	sachip->gc.set = sa1111_gpio_set;
61762306a36Sopenharmony_ci	sachip->gc.set_multiple = sa1111_gpio_set_multiple;
61862306a36Sopenharmony_ci	sachip->gc.to_irq = sa1111_gpio_to_irq;
61962306a36Sopenharmony_ci	sachip->gc.base = -1;
62062306a36Sopenharmony_ci	sachip->gc.ngpio = 18;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return devm_gpiochip_add_data(sachip->dev, &sachip->gc, sachip);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/*
62662306a36Sopenharmony_ci * Bring the SA1111 out of reset.  This requires a set procedure:
62762306a36Sopenharmony_ci *  1. nRESET asserted (by hardware)
62862306a36Sopenharmony_ci *  2. CLK turned on from SA1110
62962306a36Sopenharmony_ci *  3. nRESET deasserted
63062306a36Sopenharmony_ci *  4. VCO turned on, PLL_BYPASS turned off
63162306a36Sopenharmony_ci *  5. Wait lock time, then assert RCLKEn
63262306a36Sopenharmony_ci *  7. PCR set to allow clocking of individual functions
63362306a36Sopenharmony_ci *
63462306a36Sopenharmony_ci * Until we've done this, the only registers we can access are:
63562306a36Sopenharmony_ci *   SBI_SKCR
63662306a36Sopenharmony_ci *   SBI_SMCR
63762306a36Sopenharmony_ci *   SBI_SKID
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_cistatic void sa1111_wake(struct sa1111 *sachip)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	unsigned long flags, r;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	clk_enable(sachip->clk);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/*
64862306a36Sopenharmony_ci	 * Turn VCO on, and disable PLL Bypass.
64962306a36Sopenharmony_ci	 */
65062306a36Sopenharmony_ci	r = readl_relaxed(sachip->base + SA1111_SKCR);
65162306a36Sopenharmony_ci	r &= ~SKCR_VCO_OFF;
65262306a36Sopenharmony_ci	writel_relaxed(r, sachip->base + SA1111_SKCR);
65362306a36Sopenharmony_ci	r |= SKCR_PLL_BYPASS | SKCR_OE_EN;
65462306a36Sopenharmony_ci	writel_relaxed(r, sachip->base + SA1111_SKCR);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/*
65762306a36Sopenharmony_ci	 * Wait lock time.  SA1111 manual _doesn't_
65862306a36Sopenharmony_ci	 * specify a figure for this!  We choose 100us.
65962306a36Sopenharmony_ci	 */
66062306a36Sopenharmony_ci	udelay(100);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/*
66362306a36Sopenharmony_ci	 * Enable RCLK.  We also ensure that RDYEN is set.
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	r |= SKCR_RCLKEN | SKCR_RDYEN;
66662306a36Sopenharmony_ci	writel_relaxed(r, sachip->base + SA1111_SKCR);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/*
66962306a36Sopenharmony_ci	 * Wait 14 RCLK cycles for the chip to finish coming out
67062306a36Sopenharmony_ci	 * of reset. (RCLK=24MHz).  This is 590ns.
67162306a36Sopenharmony_ci	 */
67262306a36Sopenharmony_ci	udelay(1);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/*
67562306a36Sopenharmony_ci	 * Ensure all clocks are initially off.
67662306a36Sopenharmony_ci	 */
67762306a36Sopenharmony_ci	writel_relaxed(0, sachip->base + SA1111_SKPCR);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci#ifdef CONFIG_ARCH_SA1100
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic u32 sa1111_dma_mask[] = {
68562306a36Sopenharmony_ci	~0,
68662306a36Sopenharmony_ci	~(1 << 20),
68762306a36Sopenharmony_ci	~(1 << 23),
68862306a36Sopenharmony_ci	~(1 << 24),
68962306a36Sopenharmony_ci	~(1 << 25),
69062306a36Sopenharmony_ci	~(1 << 20),
69162306a36Sopenharmony_ci	~(1 << 20),
69262306a36Sopenharmony_ci	0,
69362306a36Sopenharmony_ci};
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci/*
69662306a36Sopenharmony_ci * Configure the SA1111 shared memory controller.
69762306a36Sopenharmony_ci */
69862306a36Sopenharmony_cistatic void
69962306a36Sopenharmony_cisa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac,
70062306a36Sopenharmony_ci		     unsigned int cas_latency)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (cas_latency == 3)
70562306a36Sopenharmony_ci		smcr |= SMCR_CLAT;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	writel_relaxed(smcr, sachip->base + SA1111_SMCR);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/*
71062306a36Sopenharmony_ci	 * Now clear the bits in the DMA mask to work around the SA1111
71162306a36Sopenharmony_ci	 * DMA erratum (Intel StrongARM SA-1111 Microprocessor Companion
71262306a36Sopenharmony_ci	 * Chip Specification Update, June 2000, Erratum #7).
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	if (sachip->dev->dma_mask)
71562306a36Sopenharmony_ci		*sachip->dev->dma_mask &= sa1111_dma_mask[drac >> 2];
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	sachip->dev->coherent_dma_mask &= sa1111_dma_mask[drac >> 2];
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci#endif
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic void sa1111_dev_release(struct device *_dev)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct sa1111_dev *dev = to_sa1111_device(_dev);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	kfree(dev);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic int
72962306a36Sopenharmony_cisa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
73062306a36Sopenharmony_ci		      struct sa1111_dev_info *info)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct sa1111_dev *dev;
73362306a36Sopenharmony_ci	unsigned i;
73462306a36Sopenharmony_ci	int ret;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	dev = kzalloc(sizeof(struct sa1111_dev), GFP_KERNEL);
73762306a36Sopenharmony_ci	if (!dev) {
73862306a36Sopenharmony_ci		ret = -ENOMEM;
73962306a36Sopenharmony_ci		goto err_alloc;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	device_initialize(&dev->dev);
74362306a36Sopenharmony_ci	dev_set_name(&dev->dev, "%4.4lx", info->offset);
74462306a36Sopenharmony_ci	dev->devid	 = info->devid;
74562306a36Sopenharmony_ci	dev->dev.parent  = sachip->dev;
74662306a36Sopenharmony_ci	dev->dev.bus     = &sa1111_bus_type;
74762306a36Sopenharmony_ci	dev->dev.release = sa1111_dev_release;
74862306a36Sopenharmony_ci	dev->res.start   = sachip->phys + info->offset;
74962306a36Sopenharmony_ci	dev->res.end     = dev->res.start + 511;
75062306a36Sopenharmony_ci	dev->res.name    = dev_name(&dev->dev);
75162306a36Sopenharmony_ci	dev->res.flags   = IORESOURCE_MEM;
75262306a36Sopenharmony_ci	dev->mapbase     = sachip->base + info->offset;
75362306a36Sopenharmony_ci	dev->skpcr_mask  = info->skpcr_mask;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(info->hwirq); i++)
75662306a36Sopenharmony_ci		dev->hwirq[i] = info->hwirq[i];
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/*
75962306a36Sopenharmony_ci	 * If the parent device has a DMA mask associated with it, and
76062306a36Sopenharmony_ci	 * this child supports DMA, propagate it down to the children.
76162306a36Sopenharmony_ci	 */
76262306a36Sopenharmony_ci	if (info->dma && sachip->dev->dma_mask) {
76362306a36Sopenharmony_ci		dev->dma_mask = *sachip->dev->dma_mask;
76462306a36Sopenharmony_ci		dev->dev.dma_mask = &dev->dma_mask;
76562306a36Sopenharmony_ci		dev->dev.coherent_dma_mask = sachip->dev->coherent_dma_mask;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	ret = request_resource(parent, &dev->res);
76962306a36Sopenharmony_ci	if (ret) {
77062306a36Sopenharmony_ci		dev_err(sachip->dev, "failed to allocate resource for %s\n",
77162306a36Sopenharmony_ci			dev->res.name);
77262306a36Sopenharmony_ci		goto err_resource;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	ret = device_add(&dev->dev);
77662306a36Sopenharmony_ci	if (ret)
77762306a36Sopenharmony_ci		goto err_add;
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci err_add:
78162306a36Sopenharmony_ci	release_resource(&dev->res);
78262306a36Sopenharmony_ci err_resource:
78362306a36Sopenharmony_ci	put_device(&dev->dev);
78462306a36Sopenharmony_ci err_alloc:
78562306a36Sopenharmony_ci	return ret;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci/**
78962306a36Sopenharmony_ci *	sa1111_probe - probe for a single SA1111 chip.
79062306a36Sopenharmony_ci *	@phys_addr: physical address of device.
79162306a36Sopenharmony_ci *
79262306a36Sopenharmony_ci *	Probe for a SA1111 chip.  This must be called
79362306a36Sopenharmony_ci *	before any other SA1111-specific code.
79462306a36Sopenharmony_ci *
79562306a36Sopenharmony_ci *	Returns:
79662306a36Sopenharmony_ci *	%-ENODEV	device not found.
79762306a36Sopenharmony_ci *	%-EBUSY		physical address already marked in-use.
79862306a36Sopenharmony_ci *	%-EINVAL	no platform data passed
79962306a36Sopenharmony_ci *	%0		successful.
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_cistatic int __sa1111_probe(struct device *me, struct resource *mem, int irq)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct sa1111_platform_data *pd = me->platform_data;
80462306a36Sopenharmony_ci	struct sa1111 *sachip;
80562306a36Sopenharmony_ci	unsigned long id;
80662306a36Sopenharmony_ci	unsigned int has_devs;
80762306a36Sopenharmony_ci	int i, ret = -ENODEV;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (!pd)
81062306a36Sopenharmony_ci		return -EINVAL;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	sachip = devm_kzalloc(me, sizeof(struct sa1111), GFP_KERNEL);
81362306a36Sopenharmony_ci	if (!sachip)
81462306a36Sopenharmony_ci		return -ENOMEM;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	sachip->clk = devm_clk_get(me, "SA1111_CLK");
81762306a36Sopenharmony_ci	if (IS_ERR(sachip->clk))
81862306a36Sopenharmony_ci		return PTR_ERR(sachip->clk);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ret = clk_prepare(sachip->clk);
82162306a36Sopenharmony_ci	if (ret)
82262306a36Sopenharmony_ci		return ret;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	spin_lock_init(&sachip->lock);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	sachip->dev = me;
82762306a36Sopenharmony_ci	dev_set_drvdata(sachip->dev, sachip);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	sachip->pdata = pd;
83062306a36Sopenharmony_ci	sachip->phys = mem->start;
83162306a36Sopenharmony_ci	sachip->irq = irq;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/*
83462306a36Sopenharmony_ci	 * Map the whole region.  This also maps the
83562306a36Sopenharmony_ci	 * registers for our children.
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	sachip->base = ioremap(mem->start, PAGE_SIZE * 2);
83862306a36Sopenharmony_ci	if (!sachip->base) {
83962306a36Sopenharmony_ci		ret = -ENOMEM;
84062306a36Sopenharmony_ci		goto err_clk_unprep;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * Probe for the chip.  Only touch the SBI registers.
84562306a36Sopenharmony_ci	 */
84662306a36Sopenharmony_ci	id = readl_relaxed(sachip->base + SA1111_SKID);
84762306a36Sopenharmony_ci	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
84862306a36Sopenharmony_ci		printk(KERN_DEBUG "SA1111 not detected: ID = %08lx\n", id);
84962306a36Sopenharmony_ci		ret = -ENODEV;
85062306a36Sopenharmony_ci		goto err_unmap;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	pr_info("SA1111 Microprocessor Companion Chip: silicon revision %lx, metal revision %lx\n",
85462306a36Sopenharmony_ci		(id & SKID_SIREV_MASK) >> 4, id & SKID_MTREV_MASK);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/*
85762306a36Sopenharmony_ci	 * We found it.  Wake the chip up, and initialise.
85862306a36Sopenharmony_ci	 */
85962306a36Sopenharmony_ci	sa1111_wake(sachip);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/*
86262306a36Sopenharmony_ci	 * The interrupt controller must be initialised before any
86362306a36Sopenharmony_ci	 * other device to ensure that the interrupts are available.
86462306a36Sopenharmony_ci	 */
86562306a36Sopenharmony_ci	ret = sa1111_setup_irq(sachip, pd->irq_base);
86662306a36Sopenharmony_ci	if (ret)
86762306a36Sopenharmony_ci		goto err_clk;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/* Setup the GPIOs - should really be done after the IRQ setup */
87062306a36Sopenharmony_ci	ret = sa1111_setup_gpios(sachip);
87162306a36Sopenharmony_ci	if (ret)
87262306a36Sopenharmony_ci		goto err_irq;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci#ifdef CONFIG_ARCH_SA1100
87562306a36Sopenharmony_ci	{
87662306a36Sopenharmony_ci	unsigned int val;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/*
87962306a36Sopenharmony_ci	 * The SDRAM configuration of the SA1110 and the SA1111 must
88062306a36Sopenharmony_ci	 * match.  This is very important to ensure that SA1111 accesses
88162306a36Sopenharmony_ci	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
88262306a36Sopenharmony_ci	 * MBGNT signal, so we must have called sa1110_mb_disable()
88362306a36Sopenharmony_ci	 * beforehand.
88462306a36Sopenharmony_ci	 */
88562306a36Sopenharmony_ci	sa1111_configure_smc(sachip, 1,
88662306a36Sopenharmony_ci			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
88762306a36Sopenharmony_ci			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/*
89062306a36Sopenharmony_ci	 * We only need to turn on DCLK whenever we want to use the
89162306a36Sopenharmony_ci	 * DMA.  It can otherwise be held firmly in the off position.
89262306a36Sopenharmony_ci	 * (currently, we always enable it.)
89362306a36Sopenharmony_ci	 */
89462306a36Sopenharmony_ci	val = readl_relaxed(sachip->base + SA1111_SKPCR);
89562306a36Sopenharmony_ci	writel_relaxed(val | SKPCR_DCLKEN, sachip->base + SA1111_SKPCR);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/*
89862306a36Sopenharmony_ci	 * Enable the SA1110 memory bus request and grant signals.
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	sa1110_mb_enable();
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci#endif
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	g_sa1111 = sachip;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	has_devs = ~0;
90762306a36Sopenharmony_ci	if (pd)
90862306a36Sopenharmony_ci		has_devs &= ~pd->disable_devs;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sa1111_devices); i++)
91162306a36Sopenharmony_ci		if (sa1111_devices[i].devid & has_devs)
91262306a36Sopenharmony_ci			sa1111_init_one_child(sachip, mem, &sa1111_devices[i]);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci err_irq:
91762306a36Sopenharmony_ci	sa1111_remove_irq(sachip);
91862306a36Sopenharmony_ci err_clk:
91962306a36Sopenharmony_ci	clk_disable(sachip->clk);
92062306a36Sopenharmony_ci err_unmap:
92162306a36Sopenharmony_ci	iounmap(sachip->base);
92262306a36Sopenharmony_ci err_clk_unprep:
92362306a36Sopenharmony_ci	clk_unprepare(sachip->clk);
92462306a36Sopenharmony_ci	return ret;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic int sa1111_remove_one(struct device *dev, void *data)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct sa1111_dev *sadev = to_sa1111_device(dev);
93062306a36Sopenharmony_ci	if (dev->bus != &sa1111_bus_type)
93162306a36Sopenharmony_ci		return 0;
93262306a36Sopenharmony_ci	device_del(&sadev->dev);
93362306a36Sopenharmony_ci	release_resource(&sadev->res);
93462306a36Sopenharmony_ci	put_device(&sadev->dev);
93562306a36Sopenharmony_ci	return 0;
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic void __sa1111_remove(struct sa1111 *sachip)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	device_for_each_child(sachip->dev, NULL, sa1111_remove_one);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	sa1111_remove_irq(sachip);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	clk_disable(sachip->clk);
94562306a36Sopenharmony_ci	clk_unprepare(sachip->clk);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	iounmap(sachip->base);
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistruct sa1111_save_data {
95162306a36Sopenharmony_ci	unsigned int	skcr;
95262306a36Sopenharmony_ci	unsigned int	skpcr;
95362306a36Sopenharmony_ci	unsigned int	skcdr;
95462306a36Sopenharmony_ci	unsigned char	skaud;
95562306a36Sopenharmony_ci	unsigned char	skpwm0;
95662306a36Sopenharmony_ci	unsigned char	skpwm1;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/*
95962306a36Sopenharmony_ci	 * Interrupt controller
96062306a36Sopenharmony_ci	 */
96162306a36Sopenharmony_ci	unsigned int	intpol0;
96262306a36Sopenharmony_ci	unsigned int	intpol1;
96362306a36Sopenharmony_ci	unsigned int	inten0;
96462306a36Sopenharmony_ci	unsigned int	inten1;
96562306a36Sopenharmony_ci	unsigned int	wakepol0;
96662306a36Sopenharmony_ci	unsigned int	wakepol1;
96762306a36Sopenharmony_ci	unsigned int	wakeen0;
96862306a36Sopenharmony_ci	unsigned int	wakeen1;
96962306a36Sopenharmony_ci};
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci#ifdef CONFIG_PM
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic int sa1111_suspend_noirq(struct device *dev)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	struct sa1111 *sachip = dev_get_drvdata(dev);
97662306a36Sopenharmony_ci	struct sa1111_save_data *save;
97762306a36Sopenharmony_ci	unsigned long flags;
97862306a36Sopenharmony_ci	unsigned int val;
97962306a36Sopenharmony_ci	void __iomem *base;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
98262306a36Sopenharmony_ci	if (!save)
98362306a36Sopenharmony_ci		return -ENOMEM;
98462306a36Sopenharmony_ci	sachip->saved_state = save;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/*
98962306a36Sopenharmony_ci	 * Save state.
99062306a36Sopenharmony_ci	 */
99162306a36Sopenharmony_ci	base = sachip->base;
99262306a36Sopenharmony_ci	save->skcr     = readl_relaxed(base + SA1111_SKCR);
99362306a36Sopenharmony_ci	save->skpcr    = readl_relaxed(base + SA1111_SKPCR);
99462306a36Sopenharmony_ci	save->skcdr    = readl_relaxed(base + SA1111_SKCDR);
99562306a36Sopenharmony_ci	save->skaud    = readl_relaxed(base + SA1111_SKAUD);
99662306a36Sopenharmony_ci	save->skpwm0   = readl_relaxed(base + SA1111_SKPWM0);
99762306a36Sopenharmony_ci	save->skpwm1   = readl_relaxed(base + SA1111_SKPWM1);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	writel_relaxed(0, sachip->base + SA1111_SKPWM0);
100062306a36Sopenharmony_ci	writel_relaxed(0, sachip->base + SA1111_SKPWM1);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	base = sachip->base + SA1111_INTC;
100362306a36Sopenharmony_ci	save->intpol0  = readl_relaxed(base + SA1111_INTPOL0);
100462306a36Sopenharmony_ci	save->intpol1  = readl_relaxed(base + SA1111_INTPOL1);
100562306a36Sopenharmony_ci	save->inten0   = readl_relaxed(base + SA1111_INTEN0);
100662306a36Sopenharmony_ci	save->inten1   = readl_relaxed(base + SA1111_INTEN1);
100762306a36Sopenharmony_ci	save->wakepol0 = readl_relaxed(base + SA1111_WAKEPOL0);
100862306a36Sopenharmony_ci	save->wakepol1 = readl_relaxed(base + SA1111_WAKEPOL1);
100962306a36Sopenharmony_ci	save->wakeen0  = readl_relaxed(base + SA1111_WAKEEN0);
101062306a36Sopenharmony_ci	save->wakeen1  = readl_relaxed(base + SA1111_WAKEEN1);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/*
101362306a36Sopenharmony_ci	 * Disable.
101462306a36Sopenharmony_ci	 */
101562306a36Sopenharmony_ci	val = readl_relaxed(sachip->base + SA1111_SKCR);
101662306a36Sopenharmony_ci	writel_relaxed(val | SKCR_SLEEP, sachip->base + SA1111_SKCR);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	clk_disable(sachip->clk);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci#ifdef CONFIG_ARCH_SA1100
102362306a36Sopenharmony_ci	sa1110_mb_disable();
102462306a36Sopenharmony_ci#endif
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return 0;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/*
103062306a36Sopenharmony_ci *	sa1111_resume - Restore the SA1111 device state.
103162306a36Sopenharmony_ci *	@dev: device to restore
103262306a36Sopenharmony_ci *
103362306a36Sopenharmony_ci *	Restore the general state of the SA1111; clock control and
103462306a36Sopenharmony_ci *	interrupt controller.  Other parts of the SA1111 must be
103562306a36Sopenharmony_ci *	restored by their respective drivers, and must be called
103662306a36Sopenharmony_ci *	via LDM after this function.
103762306a36Sopenharmony_ci */
103862306a36Sopenharmony_cistatic int sa1111_resume_noirq(struct device *dev)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct sa1111 *sachip = dev_get_drvdata(dev);
104162306a36Sopenharmony_ci	struct sa1111_save_data *save;
104262306a36Sopenharmony_ci	unsigned long flags, id;
104362306a36Sopenharmony_ci	void __iomem *base;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	save = sachip->saved_state;
104662306a36Sopenharmony_ci	if (!save)
104762306a36Sopenharmony_ci		return 0;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/*
105062306a36Sopenharmony_ci	 * Ensure that the SA1111 is still here.
105162306a36Sopenharmony_ci	 * FIXME: shouldn't do this here.
105262306a36Sopenharmony_ci	 */
105362306a36Sopenharmony_ci	id = readl_relaxed(sachip->base + SA1111_SKID);
105462306a36Sopenharmony_ci	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
105562306a36Sopenharmony_ci		__sa1111_remove(sachip);
105662306a36Sopenharmony_ci		dev_set_drvdata(dev, NULL);
105762306a36Sopenharmony_ci		kfree(save);
105862306a36Sopenharmony_ci		return 0;
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	/*
106262306a36Sopenharmony_ci	 * First of all, wake up the chip.
106362306a36Sopenharmony_ci	 */
106462306a36Sopenharmony_ci	sa1111_wake(sachip);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci#ifdef CONFIG_ARCH_SA1100
106762306a36Sopenharmony_ci	/* Enable the memory bus request/grant signals */
106862306a36Sopenharmony_ci	sa1110_mb_enable();
106962306a36Sopenharmony_ci#endif
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	/*
107262306a36Sopenharmony_ci	 * Only lock for write ops. Also, sa1111_wake must be called with
107362306a36Sopenharmony_ci	 * released spinlock!
107462306a36Sopenharmony_ci	 */
107562306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	writel_relaxed(0, sachip->base + SA1111_INTC + SA1111_INTEN0);
107862306a36Sopenharmony_ci	writel_relaxed(0, sachip->base + SA1111_INTC + SA1111_INTEN1);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	base = sachip->base;
108162306a36Sopenharmony_ci	writel_relaxed(save->skcr,     base + SA1111_SKCR);
108262306a36Sopenharmony_ci	writel_relaxed(save->skpcr,    base + SA1111_SKPCR);
108362306a36Sopenharmony_ci	writel_relaxed(save->skcdr,    base + SA1111_SKCDR);
108462306a36Sopenharmony_ci	writel_relaxed(save->skaud,    base + SA1111_SKAUD);
108562306a36Sopenharmony_ci	writel_relaxed(save->skpwm0,   base + SA1111_SKPWM0);
108662306a36Sopenharmony_ci	writel_relaxed(save->skpwm1,   base + SA1111_SKPWM1);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	base = sachip->base + SA1111_INTC;
108962306a36Sopenharmony_ci	writel_relaxed(save->intpol0,  base + SA1111_INTPOL0);
109062306a36Sopenharmony_ci	writel_relaxed(save->intpol1,  base + SA1111_INTPOL1);
109162306a36Sopenharmony_ci	writel_relaxed(save->inten0,   base + SA1111_INTEN0);
109262306a36Sopenharmony_ci	writel_relaxed(save->inten1,   base + SA1111_INTEN1);
109362306a36Sopenharmony_ci	writel_relaxed(save->wakepol0, base + SA1111_WAKEPOL0);
109462306a36Sopenharmony_ci	writel_relaxed(save->wakepol1, base + SA1111_WAKEPOL1);
109562306a36Sopenharmony_ci	writel_relaxed(save->wakeen0,  base + SA1111_WAKEEN0);
109662306a36Sopenharmony_ci	writel_relaxed(save->wakeen1,  base + SA1111_WAKEEN1);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	sachip->saved_state = NULL;
110162306a36Sopenharmony_ci	kfree(save);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return 0;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci#else
110762306a36Sopenharmony_ci#define sa1111_suspend_noirq NULL
110862306a36Sopenharmony_ci#define sa1111_resume_noirq  NULL
110962306a36Sopenharmony_ci#endif
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic int sa1111_probe(struct platform_device *pdev)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct resource *mem;
111462306a36Sopenharmony_ci	int irq;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
111762306a36Sopenharmony_ci	if (!mem)
111862306a36Sopenharmony_ci		return -EINVAL;
111962306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
112062306a36Sopenharmony_ci	if (irq < 0)
112162306a36Sopenharmony_ci		return irq;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	return __sa1111_probe(&pdev->dev, mem, irq);
112462306a36Sopenharmony_ci}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic void sa1111_remove(struct platform_device *pdev)
112762306a36Sopenharmony_ci{
112862306a36Sopenharmony_ci	struct sa1111 *sachip = platform_get_drvdata(pdev);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (sachip) {
113162306a36Sopenharmony_ci#ifdef CONFIG_PM
113262306a36Sopenharmony_ci		kfree(sachip->saved_state);
113362306a36Sopenharmony_ci		sachip->saved_state = NULL;
113462306a36Sopenharmony_ci#endif
113562306a36Sopenharmony_ci		__sa1111_remove(sachip);
113662306a36Sopenharmony_ci		platform_set_drvdata(pdev, NULL);
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic struct dev_pm_ops sa1111_pm_ops = {
114162306a36Sopenharmony_ci	.suspend_noirq = sa1111_suspend_noirq,
114262306a36Sopenharmony_ci	.resume_noirq = sa1111_resume_noirq,
114362306a36Sopenharmony_ci};
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/*
114662306a36Sopenharmony_ci *	Not sure if this should be on the system bus or not yet.
114762306a36Sopenharmony_ci *	We really want some way to register a system device at
114862306a36Sopenharmony_ci *	the per-machine level, and then have this driver pick
114962306a36Sopenharmony_ci *	up the registered devices.
115062306a36Sopenharmony_ci *
115162306a36Sopenharmony_ci *	We also need to handle the SDRAM configuration for
115262306a36Sopenharmony_ci *	PXA250/SA1110 machine classes.
115362306a36Sopenharmony_ci */
115462306a36Sopenharmony_cistatic struct platform_driver sa1111_device_driver = {
115562306a36Sopenharmony_ci	.probe		= sa1111_probe,
115662306a36Sopenharmony_ci	.remove_new	= sa1111_remove,
115762306a36Sopenharmony_ci	.driver		= {
115862306a36Sopenharmony_ci		.name	= "sa1111",
115962306a36Sopenharmony_ci		.pm	= &sa1111_pm_ops,
116062306a36Sopenharmony_ci	},
116162306a36Sopenharmony_ci};
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci *	Get the parent device driver (us) structure
116562306a36Sopenharmony_ci *	from a child function device
116662306a36Sopenharmony_ci */
116762306a36Sopenharmony_cistatic inline struct sa1111 *sa1111_chip_driver(struct sa1111_dev *sadev)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	return (struct sa1111 *)dev_get_drvdata(sadev->dev.parent);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci/*
117362306a36Sopenharmony_ci * The bits in the opdiv field are non-linear.
117462306a36Sopenharmony_ci */
117562306a36Sopenharmony_cistatic unsigned char opdiv_table[] = { 1, 4, 2, 8 };
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic unsigned int __sa1111_pll_clock(struct sa1111 *sachip)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	unsigned int skcdr, fbdiv, ipdiv, opdiv;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	skcdr = readl_relaxed(sachip->base + SA1111_SKCDR);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	fbdiv = (skcdr & 0x007f) + 2;
118462306a36Sopenharmony_ci	ipdiv = ((skcdr & 0x0f80) >> 7) + 2;
118562306a36Sopenharmony_ci	opdiv = opdiv_table[(skcdr & 0x3000) >> 12];
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	return 3686400 * fbdiv / (ipdiv * opdiv);
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci/**
119162306a36Sopenharmony_ci *	sa1111_pll_clock - return the current PLL clock frequency.
119262306a36Sopenharmony_ci *	@sadev: SA1111 function block
119362306a36Sopenharmony_ci *
119462306a36Sopenharmony_ci *	BUG: we should look at SKCR.  We also blindly believe that
119562306a36Sopenharmony_ci *	the chip is being fed with the 3.6864MHz clock.
119662306a36Sopenharmony_ci *
119762306a36Sopenharmony_ci *	Returns the PLL clock in Hz.
119862306a36Sopenharmony_ci */
119962306a36Sopenharmony_ciunsigned int sa1111_pll_clock(struct sa1111_dev *sadev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	return __sa1111_pll_clock(sachip);
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_pll_clock);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci/**
120862306a36Sopenharmony_ci *	sa1111_select_audio_mode - select I2S or AC link mode
120962306a36Sopenharmony_ci *	@sadev: SA1111 function block
121062306a36Sopenharmony_ci *	@mode: One of %SA1111_AUDIO_ACLINK or %SA1111_AUDIO_I2S
121162306a36Sopenharmony_ci *
121262306a36Sopenharmony_ci *	Frob the SKCR to select AC Link mode or I2S mode for
121362306a36Sopenharmony_ci *	the audio block.
121462306a36Sopenharmony_ci */
121562306a36Sopenharmony_civoid sa1111_select_audio_mode(struct sa1111_dev *sadev, int mode)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
121862306a36Sopenharmony_ci	unsigned long flags;
121962306a36Sopenharmony_ci	unsigned int val;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	val = readl_relaxed(sachip->base + SA1111_SKCR);
122462306a36Sopenharmony_ci	if (mode == SA1111_AUDIO_I2S) {
122562306a36Sopenharmony_ci		val &= ~SKCR_SELAC;
122662306a36Sopenharmony_ci	} else {
122762306a36Sopenharmony_ci		val |= SKCR_SELAC;
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci	writel_relaxed(val, sachip->base + SA1111_SKCR);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_select_audio_mode);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci/**
123662306a36Sopenharmony_ci *	sa1111_set_audio_rate - set the audio sample rate
123762306a36Sopenharmony_ci *	@sadev: SA1111 SAC function block
123862306a36Sopenharmony_ci *	@rate: sample rate to select
123962306a36Sopenharmony_ci */
124062306a36Sopenharmony_ciint sa1111_set_audio_rate(struct sa1111_dev *sadev, int rate)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
124362306a36Sopenharmony_ci	unsigned int div;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (sadev->devid != SA1111_DEVID_SAC)
124662306a36Sopenharmony_ci		return -EINVAL;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	div = (__sa1111_pll_clock(sachip) / 256 + rate / 2) / rate;
124962306a36Sopenharmony_ci	if (div == 0)
125062306a36Sopenharmony_ci		div = 1;
125162306a36Sopenharmony_ci	if (div > 128)
125262306a36Sopenharmony_ci		div = 128;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	writel_relaxed(div - 1, sachip->base + SA1111_SKAUD);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return 0;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_set_audio_rate);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci/**
126162306a36Sopenharmony_ci *	sa1111_get_audio_rate - get the audio sample rate
126262306a36Sopenharmony_ci *	@sadev: SA1111 SAC function block device
126362306a36Sopenharmony_ci */
126462306a36Sopenharmony_ciint sa1111_get_audio_rate(struct sa1111_dev *sadev)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
126762306a36Sopenharmony_ci	unsigned long div;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (sadev->devid != SA1111_DEVID_SAC)
127062306a36Sopenharmony_ci		return -EINVAL;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	div = readl_relaxed(sachip->base + SA1111_SKAUD) + 1;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return __sa1111_pll_clock(sachip) / (256 * div);
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_get_audio_rate);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci/*
127962306a36Sopenharmony_ci * Individual device operations.
128062306a36Sopenharmony_ci */
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci/**
128362306a36Sopenharmony_ci *	sa1111_enable_device - enable an on-chip SA1111 function block
128462306a36Sopenharmony_ci *	@sadev: SA1111 function block device to enable
128562306a36Sopenharmony_ci */
128662306a36Sopenharmony_ciint sa1111_enable_device(struct sa1111_dev *sadev)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
128962306a36Sopenharmony_ci	unsigned long flags;
129062306a36Sopenharmony_ci	unsigned int val;
129162306a36Sopenharmony_ci	int ret = 0;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (sachip->pdata && sachip->pdata->enable)
129462306a36Sopenharmony_ci		ret = sachip->pdata->enable(sachip->pdata->data, sadev->devid);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if (ret == 0) {
129762306a36Sopenharmony_ci		spin_lock_irqsave(&sachip->lock, flags);
129862306a36Sopenharmony_ci		val = readl_relaxed(sachip->base + SA1111_SKPCR);
129962306a36Sopenharmony_ci		writel_relaxed(val | sadev->skpcr_mask, sachip->base + SA1111_SKPCR);
130062306a36Sopenharmony_ci		spin_unlock_irqrestore(&sachip->lock, flags);
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci	return ret;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_enable_device);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci/**
130762306a36Sopenharmony_ci *	sa1111_disable_device - disable an on-chip SA1111 function block
130862306a36Sopenharmony_ci *	@sadev: SA1111 function block device to disable
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_civoid sa1111_disable_device(struct sa1111_dev *sadev)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
131362306a36Sopenharmony_ci	unsigned long flags;
131462306a36Sopenharmony_ci	unsigned int val;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	spin_lock_irqsave(&sachip->lock, flags);
131762306a36Sopenharmony_ci	val = readl_relaxed(sachip->base + SA1111_SKPCR);
131862306a36Sopenharmony_ci	writel_relaxed(val & ~sadev->skpcr_mask, sachip->base + SA1111_SKPCR);
131962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sachip->lock, flags);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (sachip->pdata && sachip->pdata->disable)
132262306a36Sopenharmony_ci		sachip->pdata->disable(sachip->pdata->data, sadev->devid);
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_disable_device);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ciint sa1111_get_irq(struct sa1111_dev *sadev, unsigned num)
132762306a36Sopenharmony_ci{
132862306a36Sopenharmony_ci	struct sa1111 *sachip = sa1111_chip_driver(sadev);
132962306a36Sopenharmony_ci	if (num >= ARRAY_SIZE(sadev->hwirq))
133062306a36Sopenharmony_ci		return -EINVAL;
133162306a36Sopenharmony_ci	return sa1111_map_irq(sachip, sadev->hwirq[num]);
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sa1111_get_irq);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/*
133662306a36Sopenharmony_ci *	SA1111 "Register Access Bus."
133762306a36Sopenharmony_ci *
133862306a36Sopenharmony_ci *	We model this as a regular bus type, and hang devices directly
133962306a36Sopenharmony_ci *	off this.
134062306a36Sopenharmony_ci */
134162306a36Sopenharmony_cistatic int sa1111_match(struct device *_dev, struct device_driver *_drv)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	struct sa1111_dev *dev = to_sa1111_device(_dev);
134462306a36Sopenharmony_ci	struct sa1111_driver *drv = SA1111_DRV(_drv);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return !!(dev->devid & drv->devid);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int sa1111_bus_probe(struct device *dev)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct sa1111_dev *sadev = to_sa1111_device(dev);
135262306a36Sopenharmony_ci	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
135362306a36Sopenharmony_ci	int ret = -ENODEV;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (drv->probe)
135662306a36Sopenharmony_ci		ret = drv->probe(sadev);
135762306a36Sopenharmony_ci	return ret;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic void sa1111_bus_remove(struct device *dev)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct sa1111_dev *sadev = to_sa1111_device(dev);
136362306a36Sopenharmony_ci	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (drv->remove)
136662306a36Sopenharmony_ci		drv->remove(sadev);
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistruct bus_type sa1111_bus_type = {
137062306a36Sopenharmony_ci	.name		= "sa1111-rab",
137162306a36Sopenharmony_ci	.match		= sa1111_match,
137262306a36Sopenharmony_ci	.probe		= sa1111_bus_probe,
137362306a36Sopenharmony_ci	.remove		= sa1111_bus_remove,
137462306a36Sopenharmony_ci};
137562306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_bus_type);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ciint sa1111_driver_register(struct sa1111_driver *driver)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	driver->drv.bus = &sa1111_bus_type;
138062306a36Sopenharmony_ci	return driver_register(&driver->drv);
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_driver_register);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_civoid sa1111_driver_unregister(struct sa1111_driver *driver)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	driver_unregister(&driver->drv);
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ciEXPORT_SYMBOL(sa1111_driver_unregister);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic int __init sa1111_init(void)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	int ret = bus_register(&sa1111_bus_type);
139362306a36Sopenharmony_ci	if (ret == 0)
139462306a36Sopenharmony_ci		platform_driver_register(&sa1111_device_driver);
139562306a36Sopenharmony_ci	return ret;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic void __exit sa1111_exit(void)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	platform_driver_unregister(&sa1111_device_driver);
140162306a36Sopenharmony_ci	bus_unregister(&sa1111_bus_type);
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cisubsys_initcall(sa1111_init);
140562306a36Sopenharmony_cimodule_exit(sa1111_exit);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Corporation SA1111 core driver");
140862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1409