18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/arch/arm/common/locomo.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Sharp LoCoMo support
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file contains all generic LoCoMo support.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * All initialization functions provided here are intended to be called
108c2ecf20Sopenharmony_ci * from machine specific code with proper arguments when required.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Based on sa1111.c
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/ioport.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
248c2ecf20Sopenharmony_ci#include <linux/io.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <mach/hardware.h>
278c2ecf20Sopenharmony_ci#include <asm/irq.h>
288c2ecf20Sopenharmony_ci#include <asm/mach/irq.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/hardware/locomo.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* LoCoMo Interrupts */
338c2ecf20Sopenharmony_ci#define IRQ_LOCOMO_KEY		(0)
348c2ecf20Sopenharmony_ci#define IRQ_LOCOMO_GPIO		(1)
358c2ecf20Sopenharmony_ci#define IRQ_LOCOMO_LT		(2)
368c2ecf20Sopenharmony_ci#define IRQ_LOCOMO_SPI		(3)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* M62332 output channel selection */
398c2ecf20Sopenharmony_ci#define M62332_EVR_CH	1	/* M62332 volume channel number  */
408c2ecf20Sopenharmony_ci				/*   0 : CH.1 , 1 : CH. 2        */
418c2ecf20Sopenharmony_ci/* DAC send data */
428c2ecf20Sopenharmony_ci#define	M62332_SLAVE_ADDR	0x4e	/* Slave address  */
438c2ecf20Sopenharmony_ci#define	M62332_W_BIT		0x00	/* W bit (0 only) */
448c2ecf20Sopenharmony_ci#define	M62332_SUB_ADDR		0x00	/* Sub address    */
458c2ecf20Sopenharmony_ci#define	M62332_A_BIT		0x00	/* A bit (0 only) */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* DAC setup and hold times (expressed in us) */
488c2ecf20Sopenharmony_ci#define DAC_BUS_FREE_TIME	5	/*   4.7 us */
498c2ecf20Sopenharmony_ci#define DAC_START_SETUP_TIME	5	/*   4.7 us */
508c2ecf20Sopenharmony_ci#define DAC_STOP_SETUP_TIME	4	/*   4.0 us */
518c2ecf20Sopenharmony_ci#define DAC_START_HOLD_TIME	5	/*   4.7 us */
528c2ecf20Sopenharmony_ci#define DAC_SCL_LOW_HOLD_TIME	5	/*   4.7 us */
538c2ecf20Sopenharmony_ci#define DAC_SCL_HIGH_HOLD_TIME	4	/*   4.0 us */
548c2ecf20Sopenharmony_ci#define DAC_DATA_SETUP_TIME	1	/*   250 ns */
558c2ecf20Sopenharmony_ci#define DAC_DATA_HOLD_TIME	1	/*   300 ns */
568c2ecf20Sopenharmony_ci#define DAC_LOW_SETUP_TIME	1	/*   300 ns */
578c2ecf20Sopenharmony_ci#define DAC_HIGH_SETUP_TIME	1	/*  1000 ns */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* the following is the overall data for the locomo chip */
608c2ecf20Sopenharmony_cistruct locomo {
618c2ecf20Sopenharmony_ci	struct device *dev;
628c2ecf20Sopenharmony_ci	unsigned long phys;
638c2ecf20Sopenharmony_ci	unsigned int irq;
648c2ecf20Sopenharmony_ci	int irq_base;
658c2ecf20Sopenharmony_ci	spinlock_t lock;
668c2ecf20Sopenharmony_ci	void __iomem *base;
678c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
688c2ecf20Sopenharmony_ci	void *saved_state;
698c2ecf20Sopenharmony_ci#endif
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct locomo_dev_info {
738c2ecf20Sopenharmony_ci	unsigned long	offset;
748c2ecf20Sopenharmony_ci	unsigned long	length;
758c2ecf20Sopenharmony_ci	unsigned int	devid;
768c2ecf20Sopenharmony_ci	unsigned int	irq[1];
778c2ecf20Sopenharmony_ci	const char *	name;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* All the locomo devices.  If offset is non-zero, the mapbase for the
818c2ecf20Sopenharmony_ci * locomo_dev will be set to the chip base plus offset.  If offset is
828c2ecf20Sopenharmony_ci * zero, then the mapbase for the locomo_dev will be set to zero.  An
838c2ecf20Sopenharmony_ci * offset of zero means the device only uses GPIOs or other helper
848c2ecf20Sopenharmony_ci * functions inside this file */
858c2ecf20Sopenharmony_cistatic struct locomo_dev_info locomo_devices[] = {
868c2ecf20Sopenharmony_ci	{
878c2ecf20Sopenharmony_ci		.devid 		= LOCOMO_DEVID_KEYBOARD,
888c2ecf20Sopenharmony_ci		.irq		= { IRQ_LOCOMO_KEY },
898c2ecf20Sopenharmony_ci		.name		= "locomo-keyboard",
908c2ecf20Sopenharmony_ci		.offset		= LOCOMO_KEYBOARD,
918c2ecf20Sopenharmony_ci		.length		= 16,
928c2ecf20Sopenharmony_ci	},
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_FRONTLIGHT,
958c2ecf20Sopenharmony_ci		.irq		= {},
968c2ecf20Sopenharmony_ci		.name		= "locomo-frontlight",
978c2ecf20Sopenharmony_ci		.offset		= LOCOMO_FRONTLIGHT,
988c2ecf20Sopenharmony_ci		.length		= 8,
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	},
1018c2ecf20Sopenharmony_ci	{
1028c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_BACKLIGHT,
1038c2ecf20Sopenharmony_ci		.irq		= {},
1048c2ecf20Sopenharmony_ci		.name		= "locomo-backlight",
1058c2ecf20Sopenharmony_ci		.offset		= LOCOMO_BACKLIGHT,
1068c2ecf20Sopenharmony_ci		.length		= 8,
1078c2ecf20Sopenharmony_ci	},
1088c2ecf20Sopenharmony_ci	{
1098c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_AUDIO,
1108c2ecf20Sopenharmony_ci		.irq		= {},
1118c2ecf20Sopenharmony_ci		.name		= "locomo-audio",
1128c2ecf20Sopenharmony_ci		.offset		= LOCOMO_AUDIO,
1138c2ecf20Sopenharmony_ci		.length		= 4,
1148c2ecf20Sopenharmony_ci	},
1158c2ecf20Sopenharmony_ci	{
1168c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_LED,
1178c2ecf20Sopenharmony_ci		.irq 		= {},
1188c2ecf20Sopenharmony_ci		.name		= "locomo-led",
1198c2ecf20Sopenharmony_ci		.offset		= LOCOMO_LED,
1208c2ecf20Sopenharmony_ci		.length		= 8,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	{
1238c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_UART,
1248c2ecf20Sopenharmony_ci		.irq		= {},
1258c2ecf20Sopenharmony_ci		.name		= "locomo-uart",
1268c2ecf20Sopenharmony_ci		.offset		= 0,
1278c2ecf20Sopenharmony_ci		.length		= 0,
1288c2ecf20Sopenharmony_ci	},
1298c2ecf20Sopenharmony_ci	{
1308c2ecf20Sopenharmony_ci		.devid		= LOCOMO_DEVID_SPI,
1318c2ecf20Sopenharmony_ci		.irq		= {},
1328c2ecf20Sopenharmony_ci		.name		= "locomo-spi",
1338c2ecf20Sopenharmony_ci		.offset		= LOCOMO_SPI,
1348c2ecf20Sopenharmony_ci		.length		= 0x30,
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void locomo_handler(struct irq_desc *desc)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct locomo *lchip = irq_desc_get_handler_data(desc);
1418c2ecf20Sopenharmony_ci	int req, i;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Acknowledge the parent IRQ */
1448c2ecf20Sopenharmony_ci	desc->irq_data.chip->irq_ack(&desc->irq_data);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* check why this interrupt was generated */
1478c2ecf20Sopenharmony_ci	req = locomo_readl(lchip->base + LOCOMO_ICR) & 0x0f00;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (req) {
1508c2ecf20Sopenharmony_ci		unsigned int irq;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		/* generate the next interrupt(s) */
1538c2ecf20Sopenharmony_ci		irq = lchip->irq_base;
1548c2ecf20Sopenharmony_ci		for (i = 0; i <= 3; i++, irq++) {
1558c2ecf20Sopenharmony_ci			if (req & (0x0100 << i)) {
1568c2ecf20Sopenharmony_ci				generic_handle_irq(irq);
1578c2ecf20Sopenharmony_ci			}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void locomo_ack_irq(struct irq_data *d)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void locomo_mask_irq(struct irq_data *d)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct locomo *lchip = irq_data_get_irq_chip_data(d);
1708c2ecf20Sopenharmony_ci	unsigned int r;
1718c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_ICR);
1728c2ecf20Sopenharmony_ci	r &= ~(0x0010 << (d->irq - lchip->irq_base));
1738c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_ICR);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void locomo_unmask_irq(struct irq_data *d)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct locomo *lchip = irq_data_get_irq_chip_data(d);
1798c2ecf20Sopenharmony_ci	unsigned int r;
1808c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_ICR);
1818c2ecf20Sopenharmony_ci	r |= (0x0010 << (d->irq - lchip->irq_base));
1828c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_ICR);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic struct irq_chip locomo_chip = {
1868c2ecf20Sopenharmony_ci	.name		= "LOCOMO",
1878c2ecf20Sopenharmony_ci	.irq_ack	= locomo_ack_irq,
1888c2ecf20Sopenharmony_ci	.irq_mask	= locomo_mask_irq,
1898c2ecf20Sopenharmony_ci	.irq_unmask	= locomo_unmask_irq,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void locomo_setup_irq(struct locomo *lchip)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	int irq = lchip->irq_base;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/*
1978c2ecf20Sopenharmony_ci	 * Install handler for IRQ_LOCOMO_HW.
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	irq_set_irq_type(lchip->irq, IRQ_TYPE_EDGE_FALLING);
2008c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(lchip->irq, locomo_handler, lchip);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Install handlers for IRQ_LOCOMO_* */
2038c2ecf20Sopenharmony_ci	for ( ; irq <= lchip->irq_base + 3; irq++) {
2048c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(irq, &locomo_chip, handle_level_irq);
2058c2ecf20Sopenharmony_ci		irq_set_chip_data(irq, lchip);
2068c2ecf20Sopenharmony_ci		irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void locomo_dev_release(struct device *_dev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct locomo_dev *dev = LOCOMO_DEV(_dev);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	kfree(dev);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int
2198c2ecf20Sopenharmony_cilocomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct locomo_dev *dev;
2228c2ecf20Sopenharmony_ci	int ret;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(struct locomo_dev), GFP_KERNEL);
2258c2ecf20Sopenharmony_ci	if (!dev) {
2268c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2278c2ecf20Sopenharmony_ci		goto out;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/*
2318c2ecf20Sopenharmony_ci	 * If the parent device has a DMA mask associated with it,
2328c2ecf20Sopenharmony_ci	 * propagate it down to the children.
2338c2ecf20Sopenharmony_ci	 */
2348c2ecf20Sopenharmony_ci	if (lchip->dev->dma_mask) {
2358c2ecf20Sopenharmony_ci		dev->dma_mask = *lchip->dev->dma_mask;
2368c2ecf20Sopenharmony_ci		dev->dev.dma_mask = &dev->dma_mask;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	dev_set_name(&dev->dev, "%s", info->name);
2408c2ecf20Sopenharmony_ci	dev->devid	 = info->devid;
2418c2ecf20Sopenharmony_ci	dev->dev.parent  = lchip->dev;
2428c2ecf20Sopenharmony_ci	dev->dev.bus     = &locomo_bus_type;
2438c2ecf20Sopenharmony_ci	dev->dev.release = locomo_dev_release;
2448c2ecf20Sopenharmony_ci	dev->dev.coherent_dma_mask = lchip->dev->coherent_dma_mask;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (info->offset)
2478c2ecf20Sopenharmony_ci		dev->mapbase = lchip->base + info->offset;
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		dev->mapbase = 0;
2508c2ecf20Sopenharmony_ci	dev->length = info->length;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	dev->irq[0] = (lchip->irq_base == NO_IRQ) ?
2538c2ecf20Sopenharmony_ci			NO_IRQ : lchip->irq_base + info->irq[0];
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	ret = device_register(&dev->dev);
2568c2ecf20Sopenharmony_ci	if (ret) {
2578c2ecf20Sopenharmony_ci out:
2588c2ecf20Sopenharmony_ci		kfree(dev);
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci	return ret;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistruct locomo_save_data {
2668c2ecf20Sopenharmony_ci	u16	LCM_GPO;
2678c2ecf20Sopenharmony_ci	u16	LCM_SPICT;
2688c2ecf20Sopenharmony_ci	u16	LCM_GPE;
2698c2ecf20Sopenharmony_ci	u16	LCM_ASD;
2708c2ecf20Sopenharmony_ci	u16	LCM_SPIMD;
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int locomo_suspend(struct platform_device *dev, pm_message_t state)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct locomo *lchip = platform_get_drvdata(dev);
2768c2ecf20Sopenharmony_ci	struct locomo_save_data *save;
2778c2ecf20Sopenharmony_ci	unsigned long flags;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL);
2808c2ecf20Sopenharmony_ci	if (!save)
2818c2ecf20Sopenharmony_ci		return -ENOMEM;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	lchip->saved_state = save;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	save->LCM_GPO     = locomo_readl(lchip->base + LOCOMO_GPO);	/* GPIO */
2888c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_GPO);
2898c2ecf20Sopenharmony_ci	save->LCM_SPICT   = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPICT);	/* SPI */
2908c2ecf20Sopenharmony_ci	locomo_writel(0x40, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
2918c2ecf20Sopenharmony_ci	save->LCM_GPE     = locomo_readl(lchip->base + LOCOMO_GPE);	/* GPIO */
2928c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_GPE);
2938c2ecf20Sopenharmony_ci	save->LCM_ASD     = locomo_readl(lchip->base + LOCOMO_ASD);	/* ADSTART */
2948c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_ASD);
2958c2ecf20Sopenharmony_ci	save->LCM_SPIMD   = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);	/* SPI */
2968c2ecf20Sopenharmony_ci	locomo_writel(0x3C14, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_PAIF);
2998c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_DAC);
3008c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_BACKLIGHT + LOCOMO_TC);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if ((locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT0) & 0x88) && (locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT1) & 0x88))
3038c2ecf20Sopenharmony_ci		locomo_writel(0x00, lchip->base + LOCOMO_C32K); 	/* CLK32 off */
3048c2ecf20Sopenharmony_ci	else
3058c2ecf20Sopenharmony_ci		/* 18MHz already enabled, so no wait */
3068c2ecf20Sopenharmony_ci		locomo_writel(0xc1, lchip->base + LOCOMO_C32K); 	/* CLK32 on */
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_TADC);		/* 18MHz clock off*/
3098c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_AUDIO + LOCOMO_ACC);			/* 22MHz/24MHz clock off */
3108c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);			/* FL */
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int locomo_resume(struct platform_device *dev)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct locomo *lchip = platform_get_drvdata(dev);
3208c2ecf20Sopenharmony_ci	struct locomo_save_data *save;
3218c2ecf20Sopenharmony_ci	unsigned long r;
3228c2ecf20Sopenharmony_ci	unsigned long flags;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	save = lchip->saved_state;
3258c2ecf20Sopenharmony_ci	if (!save)
3268c2ecf20Sopenharmony_ci		return 0;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	locomo_writel(save->LCM_GPO, lchip->base + LOCOMO_GPO);
3318c2ecf20Sopenharmony_ci	locomo_writel(save->LCM_SPICT, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
3328c2ecf20Sopenharmony_ci	locomo_writel(save->LCM_GPE, lchip->base + LOCOMO_GPE);
3338c2ecf20Sopenharmony_ci	locomo_writel(save->LCM_ASD, lchip->base + LOCOMO_ASD);
3348c2ecf20Sopenharmony_ci	locomo_writel(save->LCM_SPIMD, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	locomo_writel(0x00, lchip->base + LOCOMO_C32K);
3378c2ecf20Sopenharmony_ci	locomo_writel(0x90, lchip->base + LOCOMO_TADC);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KSC);
3408c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
3418c2ecf20Sopenharmony_ci	r &= 0xFEFF;
3428c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
3438c2ecf20Sopenharmony_ci	locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	lchip->saved_state = NULL;
3488c2ecf20Sopenharmony_ci	kfree(save);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci#endif
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/**
3568c2ecf20Sopenharmony_ci *	locomo_probe - probe for a single LoCoMo chip.
3578c2ecf20Sopenharmony_ci *	@phys_addr: physical address of device.
3588c2ecf20Sopenharmony_ci *
3598c2ecf20Sopenharmony_ci *	Probe for a LoCoMo chip.  This must be called
3608c2ecf20Sopenharmony_ci *	before any other locomo-specific code.
3618c2ecf20Sopenharmony_ci *
3628c2ecf20Sopenharmony_ci *	Returns:
3638c2ecf20Sopenharmony_ci *	%-ENODEV	device not found.
3648c2ecf20Sopenharmony_ci *	%-EBUSY		physical address already marked in-use.
3658c2ecf20Sopenharmony_ci *	%0		successful.
3668c2ecf20Sopenharmony_ci */
3678c2ecf20Sopenharmony_cistatic int
3688c2ecf20Sopenharmony_ci__locomo_probe(struct device *me, struct resource *mem, int irq)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct locomo_platform_data *pdata = me->platform_data;
3718c2ecf20Sopenharmony_ci	struct locomo *lchip;
3728c2ecf20Sopenharmony_ci	unsigned long r;
3738c2ecf20Sopenharmony_ci	int i, ret = -ENODEV;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	lchip = kzalloc(sizeof(struct locomo), GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (!lchip)
3778c2ecf20Sopenharmony_ci		return -ENOMEM;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	spin_lock_init(&lchip->lock);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	lchip->dev = me;
3828c2ecf20Sopenharmony_ci	dev_set_drvdata(lchip->dev, lchip);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	lchip->phys = mem->start;
3858c2ecf20Sopenharmony_ci	lchip->irq = irq;
3868c2ecf20Sopenharmony_ci	lchip->irq_base = (pdata) ? pdata->irq_base : NO_IRQ;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/*
3898c2ecf20Sopenharmony_ci	 * Map the whole region.  This also maps the
3908c2ecf20Sopenharmony_ci	 * registers for our children.
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	lchip->base = ioremap(mem->start, PAGE_SIZE);
3938c2ecf20Sopenharmony_ci	if (!lchip->base) {
3948c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3958c2ecf20Sopenharmony_ci		goto out;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* locomo initialize */
3998c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_ICR);
4008c2ecf20Sopenharmony_ci	/* KEYBOARD */
4018c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* GPIO */
4048c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_GPO);
4058c2ecf20Sopenharmony_ci	locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
4068c2ecf20Sopenharmony_ci			, lchip->base + LOCOMO_GPE);
4078c2ecf20Sopenharmony_ci	locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
4088c2ecf20Sopenharmony_ci			, lchip->base + LOCOMO_GPD);
4098c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_GIE);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Frontlight */
4128c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
4138c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* Longtime timer */
4168c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_LTINT);
4178c2ecf20Sopenharmony_ci	/* SPI */
4188c2ecf20Sopenharmony_ci	locomo_writel(0, lchip->base + LOCOMO_SPI + LOCOMO_SPIIE);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	locomo_writel(6 + 8 + 320 + 30 - 10, lchip->base + LOCOMO_ASD);
4218c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_ASD);
4228c2ecf20Sopenharmony_ci	r |= 0x8000;
4238c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_ASD);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	locomo_writel(6 + 8 + 320 + 30 - 10 - 128 + 4, lchip->base + LOCOMO_HSD);
4268c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_HSD);
4278c2ecf20Sopenharmony_ci	r |= 0x8000;
4288c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_HSD);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	locomo_writel(128 / 8, lchip->base + LOCOMO_HSC);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* XON */
4338c2ecf20Sopenharmony_ci	locomo_writel(0x80, lchip->base + LOCOMO_TADC);
4348c2ecf20Sopenharmony_ci	udelay(1000);
4358c2ecf20Sopenharmony_ci	/* CLK9MEN */
4368c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_TADC);
4378c2ecf20Sopenharmony_ci	r |= 0x10;
4388c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_TADC);
4398c2ecf20Sopenharmony_ci	udelay(100);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* init DAC */
4428c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_DAC);
4438c2ecf20Sopenharmony_ci	r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
4448c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_DAC);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_VER);
4478c2ecf20Sopenharmony_ci	printk(KERN_INFO "LoCoMo Chip: %lu%lu\n", (r >> 8), (r & 0xff));
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/*
4508c2ecf20Sopenharmony_ci	 * The interrupt controller must be initialised before any
4518c2ecf20Sopenharmony_ci	 * other device to ensure that the interrupts are available.
4528c2ecf20Sopenharmony_ci	 */
4538c2ecf20Sopenharmony_ci	if (lchip->irq != NO_IRQ && lchip->irq_base != NO_IRQ)
4548c2ecf20Sopenharmony_ci		locomo_setup_irq(lchip);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(locomo_devices); i++)
4578c2ecf20Sopenharmony_ci		locomo_init_one_child(lchip, &locomo_devices[i]);
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci out:
4618c2ecf20Sopenharmony_ci	kfree(lchip);
4628c2ecf20Sopenharmony_ci	return ret;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int locomo_remove_child(struct device *dev, void *data)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	device_unregister(dev);
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic void __locomo_remove(struct locomo *lchip)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	device_for_each_child(lchip->dev, NULL, locomo_remove_child);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (lchip->irq != NO_IRQ) {
4768c2ecf20Sopenharmony_ci		irq_set_chained_handler_and_data(lchip->irq, NULL, NULL);
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	iounmap(lchip->base);
4808c2ecf20Sopenharmony_ci	kfree(lchip);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int locomo_probe(struct platform_device *dev)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct resource *mem;
4868c2ecf20Sopenharmony_ci	int irq;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
4898c2ecf20Sopenharmony_ci	if (!mem)
4908c2ecf20Sopenharmony_ci		return -EINVAL;
4918c2ecf20Sopenharmony_ci	irq = platform_get_irq(dev, 0);
4928c2ecf20Sopenharmony_ci	if (irq < 0)
4938c2ecf20Sopenharmony_ci		return -ENXIO;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return __locomo_probe(&dev->dev, mem, irq);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int locomo_remove(struct platform_device *dev)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct locomo *lchip = platform_get_drvdata(dev);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (lchip) {
5038c2ecf20Sopenharmony_ci		__locomo_remove(lchip);
5048c2ecf20Sopenharmony_ci		platform_set_drvdata(dev, NULL);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return 0;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/*
5118c2ecf20Sopenharmony_ci *	Not sure if this should be on the system bus or not yet.
5128c2ecf20Sopenharmony_ci *	We really want some way to register a system device at
5138c2ecf20Sopenharmony_ci *	the per-machine level, and then have this driver pick
5148c2ecf20Sopenharmony_ci *	up the registered devices.
5158c2ecf20Sopenharmony_ci */
5168c2ecf20Sopenharmony_cistatic struct platform_driver locomo_device_driver = {
5178c2ecf20Sopenharmony_ci	.probe		= locomo_probe,
5188c2ecf20Sopenharmony_ci	.remove		= locomo_remove,
5198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5208c2ecf20Sopenharmony_ci	.suspend	= locomo_suspend,
5218c2ecf20Sopenharmony_ci	.resume		= locomo_resume,
5228c2ecf20Sopenharmony_ci#endif
5238c2ecf20Sopenharmony_ci	.driver		= {
5248c2ecf20Sopenharmony_ci		.name	= "locomo",
5258c2ecf20Sopenharmony_ci	},
5268c2ecf20Sopenharmony_ci};
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/*
5298c2ecf20Sopenharmony_ci *	Get the parent device driver (us) structure
5308c2ecf20Sopenharmony_ci *	from a child function device
5318c2ecf20Sopenharmony_ci */
5328c2ecf20Sopenharmony_cistatic inline struct locomo *locomo_chip_driver(struct locomo_dev *ldev)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	return (struct locomo *)dev_get_drvdata(ldev->dev.parent);
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_civoid locomo_gpio_set_dir(struct device *dev, unsigned int bits, unsigned int dir)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct locomo *lchip = dev_get_drvdata(dev);
5408c2ecf20Sopenharmony_ci	unsigned long flags;
5418c2ecf20Sopenharmony_ci	unsigned int r;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (!lchip)
5448c2ecf20Sopenharmony_ci		return;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_GPD);
5498c2ecf20Sopenharmony_ci	if (dir)
5508c2ecf20Sopenharmony_ci		r |= bits;
5518c2ecf20Sopenharmony_ci	else
5528c2ecf20Sopenharmony_ci		r &= ~bits;
5538c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_GPD);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_GPE);
5568c2ecf20Sopenharmony_ci	if (dir)
5578c2ecf20Sopenharmony_ci		r |= bits;
5588c2ecf20Sopenharmony_ci	else
5598c2ecf20Sopenharmony_ci		r &= ~bits;
5608c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_GPE);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_set_dir);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ciint locomo_gpio_read_level(struct device *dev, unsigned int bits)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct locomo *lchip = dev_get_drvdata(dev);
5698c2ecf20Sopenharmony_ci	unsigned long flags;
5708c2ecf20Sopenharmony_ci	unsigned int ret;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (!lchip)
5738c2ecf20Sopenharmony_ci		return -ENODEV;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
5768c2ecf20Sopenharmony_ci	ret = locomo_readl(lchip->base + LOCOMO_GPL);
5778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	ret &= bits;
5808c2ecf20Sopenharmony_ci	return ret;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_read_level);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ciint locomo_gpio_read_output(struct device *dev, unsigned int bits)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct locomo *lchip = dev_get_drvdata(dev);
5878c2ecf20Sopenharmony_ci	unsigned long flags;
5888c2ecf20Sopenharmony_ci	unsigned int ret;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (!lchip)
5918c2ecf20Sopenharmony_ci		return -ENODEV;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
5948c2ecf20Sopenharmony_ci	ret = locomo_readl(lchip->base + LOCOMO_GPO);
5958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	ret &= bits;
5988c2ecf20Sopenharmony_ci	return ret;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_read_output);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_civoid locomo_gpio_write(struct device *dev, unsigned int bits, unsigned int set)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct locomo *lchip = dev_get_drvdata(dev);
6058c2ecf20Sopenharmony_ci	unsigned long flags;
6068c2ecf20Sopenharmony_ci	unsigned int r;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (!lchip)
6098c2ecf20Sopenharmony_ci		return;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	r = locomo_readl(lchip->base + LOCOMO_GPO);
6148c2ecf20Sopenharmony_ci	if (set)
6158c2ecf20Sopenharmony_ci		r |= bits;
6168c2ecf20Sopenharmony_ci	else
6178c2ecf20Sopenharmony_ci		r &= ~bits;
6188c2ecf20Sopenharmony_ci	locomo_writel(r, lchip->base + LOCOMO_GPO);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_write);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic void locomo_m62332_sendbit(void *mapbase, int bit)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	unsigned int r;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6298c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
6308c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6318c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
6328c2ecf20Sopenharmony_ci	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */
6338c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6348c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
6358c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6368c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
6378c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (bit & 1) {
6408c2ecf20Sopenharmony_ci		r = locomo_readl(mapbase + LOCOMO_DAC);
6418c2ecf20Sopenharmony_ci		r |=  LOCOMO_DAC_SDAOEB;
6428c2ecf20Sopenharmony_ci		locomo_writel(r, mapbase + LOCOMO_DAC);
6438c2ecf20Sopenharmony_ci		udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
6448c2ecf20Sopenharmony_ci	} else {
6458c2ecf20Sopenharmony_ci		r = locomo_readl(mapbase + LOCOMO_DAC);
6468c2ecf20Sopenharmony_ci		r &=  ~(LOCOMO_DAC_SDAOEB);
6478c2ecf20Sopenharmony_ci		locomo_writel(r, mapbase + LOCOMO_DAC);
6488c2ecf20Sopenharmony_ci		udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	udelay(DAC_DATA_SETUP_TIME);	/* 250 nsec */
6528c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6538c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB;
6548c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6558c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
6568c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/*  4.0 usec */
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_civoid locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct locomo *lchip = locomo_chip_driver(ldev);
6628c2ecf20Sopenharmony_ci	int i;
6638c2ecf20Sopenharmony_ci	unsigned char data;
6648c2ecf20Sopenharmony_ci	unsigned int r;
6658c2ecf20Sopenharmony_ci	void *mapbase = lchip->base;
6668c2ecf20Sopenharmony_ci	unsigned long flags;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* Start */
6718c2ecf20Sopenharmony_ci	udelay(DAC_BUS_FREE_TIME);	/* 5.0 usec */
6728c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6738c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
6748c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6758c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
6768c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.0 usec */
6778c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6788c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SDAOEB);
6798c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6808c2ecf20Sopenharmony_ci	udelay(DAC_START_HOLD_TIME);	/* 5.0 usec */
6818c2ecf20Sopenharmony_ci	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* Send slave address and W bit (LSB is W bit) */
6848c2ecf20Sopenharmony_ci	data = (M62332_SLAVE_ADDR << 1) | M62332_W_BIT;
6858c2ecf20Sopenharmony_ci	for (i = 1; i <= 8; i++) {
6868c2ecf20Sopenharmony_ci		locomo_m62332_sendbit(mapbase, data >> (8 - i));
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* Check A bit */
6908c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6918c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
6928c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6938c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
6948c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
6958c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
6968c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SDAOEB);
6978c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
6988c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
6998c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7008c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB;
7018c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7028c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
7038c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
7048c2ecf20Sopenharmony_ci	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
7058c2ecf20Sopenharmony_ci		printk(KERN_WARNING "locomo: m62332_senddata Error 1\n");
7068c2ecf20Sopenharmony_ci		goto out;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/* Send Sub address (LSB is channel select) */
7108c2ecf20Sopenharmony_ci	/*    channel = 0 : ch1 select              */
7118c2ecf20Sopenharmony_ci	/*            = 1 : ch2 select              */
7128c2ecf20Sopenharmony_ci	data = M62332_SUB_ADDR + channel;
7138c2ecf20Sopenharmony_ci	for (i = 1; i <= 8; i++) {
7148c2ecf20Sopenharmony_ci		locomo_m62332_sendbit(mapbase, data >> (8 - i));
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Check A bit */
7188c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7198c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
7208c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7218c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
7228c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
7238c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7248c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SDAOEB);
7258c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7268c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
7278c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7288c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB;
7298c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7308c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
7318c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
7328c2ecf20Sopenharmony_ci	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
7338c2ecf20Sopenharmony_ci		printk(KERN_WARNING "locomo: m62332_senddata Error 2\n");
7348c2ecf20Sopenharmony_ci		goto out;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Send DAC data */
7388c2ecf20Sopenharmony_ci	for (i = 1; i <= 8; i++) {
7398c2ecf20Sopenharmony_ci		locomo_m62332_sendbit(mapbase, dac_data >> (8 - i));
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* Check A bit */
7438c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7448c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
7458c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7468c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
7478c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
7488c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7498c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SDAOEB);
7508c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7518c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
7528c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7538c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB;
7548c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7558c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
7568c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
7578c2ecf20Sopenharmony_ci	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
7588c2ecf20Sopenharmony_ci		printk(KERN_WARNING "locomo: m62332_senddata Error 3\n");
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciout:
7628c2ecf20Sopenharmony_ci	/* stop */
7638c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7648c2ecf20Sopenharmony_ci	r &=  ~(LOCOMO_DAC_SCLOEB);
7658c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7668c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
7678c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
7688c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7698c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB;
7708c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7718c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
7728c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */
7738c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7748c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SDAOEB;
7758c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7768c2ecf20Sopenharmony_ci	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
7778c2ecf20Sopenharmony_ci	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	r = locomo_readl(mapbase + LOCOMO_DAC);
7808c2ecf20Sopenharmony_ci	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
7818c2ecf20Sopenharmony_ci	locomo_writel(r, mapbase + LOCOMO_DAC);
7828c2ecf20Sopenharmony_ci	udelay(DAC_LOW_SETUP_TIME);	/* 1000 nsec */
7838c2ecf20Sopenharmony_ci	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_m62332_senddata);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci/*
7908c2ecf20Sopenharmony_ci *	Frontlight control
7918c2ecf20Sopenharmony_ci */
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_civoid locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	unsigned long flags;
7968c2ecf20Sopenharmony_ci	struct locomo *lchip = locomo_chip_driver(dev);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	if (vr)
7998c2ecf20Sopenharmony_ci		locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 1);
8008c2ecf20Sopenharmony_ci	else
8018c2ecf20Sopenharmony_ci		locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lchip->lock, flags);
8048c2ecf20Sopenharmony_ci	locomo_writel(bpwf, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
8058c2ecf20Sopenharmony_ci	udelay(100);
8068c2ecf20Sopenharmony_ci	locomo_writel(duty, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
8078c2ecf20Sopenharmony_ci	locomo_writel(bpwf | LOCOMO_ALC_EN, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
8088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lchip->lock, flags);
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_frontlight_set);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/*
8138c2ecf20Sopenharmony_ci *	LoCoMo "Register Access Bus."
8148c2ecf20Sopenharmony_ci *
8158c2ecf20Sopenharmony_ci *	We model this as a regular bus type, and hang devices directly
8168c2ecf20Sopenharmony_ci *	off this.
8178c2ecf20Sopenharmony_ci */
8188c2ecf20Sopenharmony_cistatic int locomo_match(struct device *_dev, struct device_driver *_drv)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct locomo_dev *dev = LOCOMO_DEV(_dev);
8218c2ecf20Sopenharmony_ci	struct locomo_driver *drv = LOCOMO_DRV(_drv);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return dev->devid == drv->devid;
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic int locomo_bus_probe(struct device *dev)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	struct locomo_dev *ldev = LOCOMO_DEV(dev);
8298c2ecf20Sopenharmony_ci	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
8308c2ecf20Sopenharmony_ci	int ret = -ENODEV;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (drv->probe)
8338c2ecf20Sopenharmony_ci		ret = drv->probe(ldev);
8348c2ecf20Sopenharmony_ci	return ret;
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic int locomo_bus_remove(struct device *dev)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct locomo_dev *ldev = LOCOMO_DEV(dev);
8408c2ecf20Sopenharmony_ci	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
8418c2ecf20Sopenharmony_ci	int ret = 0;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	if (drv->remove)
8448c2ecf20Sopenharmony_ci		ret = drv->remove(ldev);
8458c2ecf20Sopenharmony_ci	return ret;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistruct bus_type locomo_bus_type = {
8498c2ecf20Sopenharmony_ci	.name		= "locomo-bus",
8508c2ecf20Sopenharmony_ci	.match		= locomo_match,
8518c2ecf20Sopenharmony_ci	.probe		= locomo_bus_probe,
8528c2ecf20Sopenharmony_ci	.remove		= locomo_bus_remove,
8538c2ecf20Sopenharmony_ci};
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ciint locomo_driver_register(struct locomo_driver *driver)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	driver->drv.bus = &locomo_bus_type;
8588c2ecf20Sopenharmony_ci	return driver_register(&driver->drv);
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_driver_register);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_civoid locomo_driver_unregister(struct locomo_driver *driver)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	driver_unregister(&driver->drv);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomo_driver_unregister);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic int __init locomo_init(void)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	int ret = bus_register(&locomo_bus_type);
8718c2ecf20Sopenharmony_ci	if (ret == 0)
8728c2ecf20Sopenharmony_ci		platform_driver_register(&locomo_device_driver);
8738c2ecf20Sopenharmony_ci	return ret;
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_cistatic void __exit locomo_exit(void)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	platform_driver_unregister(&locomo_device_driver);
8798c2ecf20Sopenharmony_ci	bus_unregister(&locomo_bus_type);
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cimodule_init(locomo_init);
8838c2ecf20Sopenharmony_cimodule_exit(locomo_exit);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sharp LoCoMo core driver");
8868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8878c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
888