locomo.c 23.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * linux/arch/arm/common/locomo.c
 *
 * Sharp LoCoMo support
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This file contains all generic LoCoMo support.
 *
 * All initialization functions provided here are intended to be called
 * from machine specific code with proper arguments when required.
 *
 * Based on sa1111.c
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
24
#include <linux/platform_device.h>
Linus Torvalds's avatar
Linus Torvalds committed
25 26
#include <linux/slab.h>
#include <linux/spinlock.h>
27
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
28

29
#include <mach/hardware.h>
Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33 34
#include <asm/irq.h>
#include <asm/mach/irq.h>

#include <asm/hardware/locomo.h>

35 36 37 38 39 40
/* LoCoMo Interrupts */
#define IRQ_LOCOMO_KEY		(0)
#define IRQ_LOCOMO_GPIO		(1)
#define IRQ_LOCOMO_LT		(2)
#define IRQ_LOCOMO_SPI		(3)

Linus Torvalds's avatar
Linus Torvalds committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/* M62332 output channel selection */
#define M62332_EVR_CH	1	/* M62332 volume channel number  */
				/*   0 : CH.1 , 1 : CH. 2        */
/* DAC send data */
#define	M62332_SLAVE_ADDR	0x4e	/* Slave address  */
#define	M62332_W_BIT		0x00	/* W bit (0 only) */
#define	M62332_SUB_ADDR		0x00	/* Sub address    */
#define	M62332_A_BIT		0x00	/* A bit (0 only) */

/* DAC setup and hold times (expressed in us) */
#define DAC_BUS_FREE_TIME	5	/*   4.7 us */
#define DAC_START_SETUP_TIME	5	/*   4.7 us */
#define DAC_STOP_SETUP_TIME	4	/*   4.0 us */
#define DAC_START_HOLD_TIME	5	/*   4.7 us */
#define DAC_SCL_LOW_HOLD_TIME	5	/*   4.7 us */
#define DAC_SCL_HIGH_HOLD_TIME	4	/*   4.0 us */
#define DAC_DATA_SETUP_TIME	1	/*   250 ns */
#define DAC_DATA_HOLD_TIME	1	/*   300 ns */
#define DAC_LOW_SETUP_TIME	1	/*   300 ns */
#define DAC_HIGH_SETUP_TIME	1	/*  1000 ns */

/* the following is the overall data for the locomo chip */
struct locomo {
	struct device *dev;
	unsigned long phys;
	unsigned int irq;
67
	int irq_base;
Linus Torvalds's avatar
Linus Torvalds committed
68
	spinlock_t lock;
69
	void __iomem *base;
70 71 72
#ifdef CONFIG_PM
	void *saved_state;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
};

struct locomo_dev_info {
	unsigned long	offset;
	unsigned long	length;
	unsigned int	devid;
	unsigned int	irq[1];
	const char *	name;
};

/* All the locomo devices.  If offset is non-zero, the mapbase for the
 * locomo_dev will be set to the chip base plus offset.  If offset is
 * zero, then the mapbase for the locomo_dev will be set to zero.  An
 * offset of zero means the device only uses GPIOs or other helper
 * functions inside this file */
static struct locomo_dev_info locomo_devices[] = {
	{
		.devid 		= LOCOMO_DEVID_KEYBOARD,
91
		.irq		= { IRQ_LOCOMO_KEY },
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
		.name		= "locomo-keyboard",
		.offset		= LOCOMO_KEYBOARD,
		.length		= 16,
	},
	{
		.devid		= LOCOMO_DEVID_FRONTLIGHT,
		.irq		= {},
		.name		= "locomo-frontlight",
		.offset		= LOCOMO_FRONTLIGHT,
		.length		= 8,

	},
	{
		.devid		= LOCOMO_DEVID_BACKLIGHT,
		.irq		= {},
		.name		= "locomo-backlight",
		.offset		= LOCOMO_BACKLIGHT,
		.length		= 8,
	},
	{
		.devid		= LOCOMO_DEVID_AUDIO,
		.irq		= {},
		.name		= "locomo-audio",
		.offset		= LOCOMO_AUDIO,
		.length		= 4,
	},
	{
		.devid		= LOCOMO_DEVID_LED,
		.irq 		= {},
		.name		= "locomo-led",
		.offset		= LOCOMO_LED,
		.length		= 8,
	},
	{
		.devid		= LOCOMO_DEVID_UART,
		.irq		= {},
		.name		= "locomo-uart",
		.offset		= 0,
		.length		= 0,
	},
132 133 134 135 136 137 138
	{
		.devid		= LOCOMO_DEVID_SPI,
		.irq		= {},
		.name		= "locomo-spi",
		.offset		= LOCOMO_SPI,
		.length		= 0x30,
	},
Linus Torvalds's avatar
Linus Torvalds committed
139 140
};

141
static void locomo_handler(struct irq_desc *desc)
Linus Torvalds's avatar
Linus Torvalds committed
142
{
143
	struct locomo *lchip = irq_desc_get_chip_data(desc);
Linus Torvalds's avatar
Linus Torvalds committed
144 145 146
	int req, i;

	/* Acknowledge the parent IRQ */
147
	desc->irq_data.chip->irq_ack(&desc->irq_data);
Linus Torvalds's avatar
Linus Torvalds committed
148 149

	/* check why this interrupt was generated */
150
	req = locomo_readl(lchip->base + LOCOMO_ICR) & 0x0f00;
Linus Torvalds's avatar
Linus Torvalds committed
151 152

	if (req) {
153 154
		unsigned int irq;

Linus Torvalds's avatar
Linus Torvalds committed
155
		/* generate the next interrupt(s) */
156
		irq = lchip->irq_base;
157
		for (i = 0; i <= 3; i++, irq++) {
Linus Torvalds's avatar
Linus Torvalds committed
158
			if (req & (0x0100 << i)) {
159
				generic_handle_irq(irq);
Linus Torvalds's avatar
Linus Torvalds committed
160 161 162 163 164 165
			}

		}
	}
}

166
static void locomo_ack_irq(struct irq_data *d)
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169
{
}

170
static void locomo_mask_irq(struct irq_data *d)
Linus Torvalds's avatar
Linus Torvalds committed
171
{
172
	struct locomo *lchip = irq_data_get_irq_chip_data(d);
Linus Torvalds's avatar
Linus Torvalds committed
173
	unsigned int r;
174
	r = locomo_readl(lchip->base + LOCOMO_ICR);
175
	r &= ~(0x0010 << (d->irq - lchip->irq_base));
176
	locomo_writel(r, lchip->base + LOCOMO_ICR);
Linus Torvalds's avatar
Linus Torvalds committed
177 178
}

179
static void locomo_unmask_irq(struct irq_data *d)
Linus Torvalds's avatar
Linus Torvalds committed
180
{
181
	struct locomo *lchip = irq_data_get_irq_chip_data(d);
Linus Torvalds's avatar
Linus Torvalds committed
182
	unsigned int r;
183
	r = locomo_readl(lchip->base + LOCOMO_ICR);
184
	r |= (0x0010 << (d->irq - lchip->irq_base));
185
	locomo_writel(r, lchip->base + LOCOMO_ICR);
Linus Torvalds's avatar
Linus Torvalds committed
186 187
}

188
static struct irq_chip locomo_chip = {
189 190 191 192
	.name		= "LOCOMO",
	.irq_ack	= locomo_ack_irq,
	.irq_mask	= locomo_mask_irq,
	.irq_unmask	= locomo_unmask_irq,
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196
};

static void locomo_setup_irq(struct locomo *lchip)
{
197
	int irq = lchip->irq_base;
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200 201

	/*
	 * Install handler for IRQ_LOCOMO_HW.
	 */
202 203 204
	irq_set_irq_type(lchip->irq, IRQ_TYPE_EDGE_FALLING);
	irq_set_chip_data(lchip->irq, lchip);
	irq_set_chained_handler(lchip->irq, locomo_handler);
Linus Torvalds's avatar
Linus Torvalds committed
205

206 207
	/* Install handlers for IRQ_LOCOMO_* */
	for ( ; irq <= lchip->irq_base + 3; irq++) {
208
		irq_set_chip_and_handler(irq, &locomo_chip, handle_level_irq);
209
		irq_set_chip_data(irq, lchip);
210
		irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	}
}


static void locomo_dev_release(struct device *_dev)
{
	struct locomo_dev *dev = LOCOMO_DEV(_dev);

	kfree(dev);
}

static int
locomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)
{
	struct locomo_dev *dev;
	int ret;

228
	dev = kzalloc(sizeof(struct locomo_dev), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242
	if (!dev) {
		ret = -ENOMEM;
		goto out;
	}

	/*
	 * If the parent device has a DMA mask associated with it,
	 * propagate it down to the children.
	 */
	if (lchip->dev->dma_mask) {
		dev->dma_mask = *lchip->dev->dma_mask;
		dev->dev.dma_mask = &dev->dma_mask;
	}

243
	dev_set_name(&dev->dev, "%s", info->name);
Linus Torvalds's avatar
Linus Torvalds committed
244 245 246 247 248 249 250 251 252 253 254 255
	dev->devid	 = info->devid;
	dev->dev.parent  = lchip->dev;
	dev->dev.bus     = &locomo_bus_type;
	dev->dev.release = locomo_dev_release;
	dev->dev.coherent_dma_mask = lchip->dev->coherent_dma_mask;

	if (info->offset)
		dev->mapbase = lchip->base + info->offset;
	else
		dev->mapbase = 0;
	dev->length = info->length;

256 257
	dev->irq[0] = (lchip->irq_base == NO_IRQ) ?
			NO_IRQ : lchip->irq_base + info->irq[0];
Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261 262 263 264 265 266

	ret = device_register(&dev->dev);
	if (ret) {
 out:
		kfree(dev);
	}
	return ret;
}

267 268 269 270 271 272 273 274 275 276
#ifdef CONFIG_PM

struct locomo_save_data {
	u16	LCM_GPO;
	u16	LCM_SPICT;
	u16	LCM_GPE;
	u16	LCM_ASD;
	u16	LCM_SPIMD;
};

277
static int locomo_suspend(struct platform_device *dev, pm_message_t state)
278
{
279
	struct locomo *lchip = platform_get_drvdata(dev);
280 281 282 283 284 285 286
	struct locomo_save_data *save;
	unsigned long flags;

	save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL);
	if (!save)
		return -ENOMEM;

287
	lchip->saved_state = save;
288 289 290 291 292

	spin_lock_irqsave(&lchip->lock, flags);

	save->LCM_GPO     = locomo_readl(lchip->base + LOCOMO_GPO);	/* GPIO */
	locomo_writel(0x00, lchip->base + LOCOMO_GPO);
293
	save->LCM_SPICT   = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPICT);	/* SPI */
294
	locomo_writel(0x40, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
295 296 297 298
	save->LCM_GPE     = locomo_readl(lchip->base + LOCOMO_GPE);	/* GPIO */
	locomo_writel(0x00, lchip->base + LOCOMO_GPE);
	save->LCM_ASD     = locomo_readl(lchip->base + LOCOMO_ASD);	/* ADSTART */
	locomo_writel(0x00, lchip->base + LOCOMO_ASD);
299 300
	save->LCM_SPIMD   = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);	/* SPI */
	locomo_writel(0x3C14, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
301 302 303 304 305

	locomo_writel(0x00, lchip->base + LOCOMO_PAIF);
	locomo_writel(0x00, lchip->base + LOCOMO_DAC);
	locomo_writel(0x00, lchip->base + LOCOMO_BACKLIGHT + LOCOMO_TC);

306
	if ((locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT0) & 0x88) && (locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT1) & 0x88))
307 308 309 310 311 312 313 314 315 316 317 318 319 320
		locomo_writel(0x00, lchip->base + LOCOMO_C32K); 	/* CLK32 off */
	else
		/* 18MHz already enabled, so no wait */
		locomo_writel(0xc1, lchip->base + LOCOMO_C32K); 	/* CLK32 on */

	locomo_writel(0x00, lchip->base + LOCOMO_TADC);		/* 18MHz clock off*/
	locomo_writel(0x00, lchip->base + LOCOMO_AUDIO + LOCOMO_ACC);			/* 22MHz/24MHz clock off */
	locomo_writel(0x00, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);			/* FL */

	spin_unlock_irqrestore(&lchip->lock, flags);

	return 0;
}

321
static int locomo_resume(struct platform_device *dev)
322
{
323
	struct locomo *lchip = platform_get_drvdata(dev);
324 325 326
	struct locomo_save_data *save;
	unsigned long r;
	unsigned long flags;
327 328

	save = lchip->saved_state;
329 330 331 332 333 334
	if (!save)
		return 0;

	spin_lock_irqsave(&lchip->lock, flags);

	locomo_writel(save->LCM_GPO, lchip->base + LOCOMO_GPO);
335
	locomo_writel(save->LCM_SPICT, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
336 337
	locomo_writel(save->LCM_GPE, lchip->base + LOCOMO_GPE);
	locomo_writel(save->LCM_ASD, lchip->base + LOCOMO_ASD);
338
	locomo_writel(save->LCM_SPIMD, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
339 340 341 342 343 344 345 346 347 348 349

	locomo_writel(0x00, lchip->base + LOCOMO_C32K);
	locomo_writel(0x90, lchip->base + LOCOMO_TADC);

	locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KSC);
	r = locomo_readl(lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
	r &= 0xFEFF;
	locomo_writel(r, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
	locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD);

	spin_unlock_irqrestore(&lchip->lock, flags);
350 351

	lchip->saved_state = NULL;
352 353 354 355 356 357
	kfree(save);

	return 0;
}
#endif

358

Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
/**
 *	locomo_probe - probe for a single LoCoMo chip.
 *	@phys_addr: physical address of device.
 *
 *	Probe for a LoCoMo chip.  This must be called
 *	before any other locomo-specific code.
 *
 *	Returns:
 *	%-ENODEV	device not found.
 *	%-EBUSY		physical address already marked in-use.
 *	%0		successful.
 */
static int
__locomo_probe(struct device *me, struct resource *mem, int irq)
{
374
	struct locomo_platform_data *pdata = me->platform_data;
Linus Torvalds's avatar
Linus Torvalds committed
375 376 377 378
	struct locomo *lchip;
	unsigned long r;
	int i, ret = -ENODEV;

379
	lchip = kzalloc(sizeof(struct locomo), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
380 381 382 383 384 385 386 387 388 389
	if (!lchip)
		return -ENOMEM;

	spin_lock_init(&lchip->lock);

	lchip->dev = me;
	dev_set_drvdata(lchip->dev, lchip);

	lchip->phys = mem->start;
	lchip->irq = irq;
390
	lchip->irq_base = (pdata) ? pdata->irq_base : NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

	/*
	 * Map the whole region.  This also maps the
	 * registers for our children.
	 */
	lchip->base = ioremap(mem->start, PAGE_SIZE);
	if (!lchip->base) {
		ret = -ENOMEM;
		goto out;
	}

	/* locomo initialize */
	locomo_writel(0, lchip->base + LOCOMO_ICR);
	/* KEYBOARD */
	locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);

	/* GPIO */
	locomo_writel(0, lchip->base + LOCOMO_GPO);
409
	locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
Linus Torvalds's avatar
Linus Torvalds committed
410
			, lchip->base + LOCOMO_GPE);
411
	locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
Linus Torvalds's avatar
Linus Torvalds committed
412 413 414
			, lchip->base + LOCOMO_GPD);
	locomo_writel(0, lchip->base + LOCOMO_GIE);

415
	/* Frontlight */
Linus Torvalds's avatar
Linus Torvalds committed
416 417
	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
418

Linus Torvalds's avatar
Linus Torvalds committed
419 420 421
	/* Longtime timer */
	locomo_writel(0, lchip->base + LOCOMO_LTINT);
	/* SPI */
422
	locomo_writel(0, lchip->base + LOCOMO_SPI + LOCOMO_SPIIE);
Linus Torvalds's avatar
Linus Torvalds committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

	locomo_writel(6 + 8 + 320 + 30 - 10, lchip->base + LOCOMO_ASD);
	r = locomo_readl(lchip->base + LOCOMO_ASD);
	r |= 0x8000;
	locomo_writel(r, lchip->base + LOCOMO_ASD);

	locomo_writel(6 + 8 + 320 + 30 - 10 - 128 + 4, lchip->base + LOCOMO_HSD);
	r = locomo_readl(lchip->base + LOCOMO_HSD);
	r |= 0x8000;
	locomo_writel(r, lchip->base + LOCOMO_HSD);

	locomo_writel(128 / 8, lchip->base + LOCOMO_HSC);

	/* XON */
	locomo_writel(0x80, lchip->base + LOCOMO_TADC);
	udelay(1000);
	/* CLK9MEN */
	r = locomo_readl(lchip->base + LOCOMO_TADC);
	r |= 0x10;
	locomo_writel(r, lchip->base + LOCOMO_TADC);
	udelay(100);

	/* init DAC */
	r = locomo_readl(lchip->base + LOCOMO_DAC);
	r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
	locomo_writel(r, lchip->base + LOCOMO_DAC);

	r = locomo_readl(lchip->base + LOCOMO_VER);
	printk(KERN_INFO "LoCoMo Chip: %lu%lu\n", (r >> 8), (r & 0xff));

	/*
	 * The interrupt controller must be initialised before any
	 * other device to ensure that the interrupts are available.
	 */
457
	if (lchip->irq != NO_IRQ && lchip->irq_base != NO_IRQ)
Linus Torvalds's avatar
Linus Torvalds committed
458 459 460 461 462 463 464 465 466 467 468
		locomo_setup_irq(lchip);

	for (i = 0; i < ARRAY_SIZE(locomo_devices); i++)
		locomo_init_one_child(lchip, &locomo_devices[i]);
	return 0;

 out:
	kfree(lchip);
	return ret;
}

469
static int locomo_remove_child(struct device *dev, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
470
{
471 472 473
	device_unregister(dev);
	return 0;
} 
Linus Torvalds's avatar
Linus Torvalds committed
474

475 476 477
static void __locomo_remove(struct locomo *lchip)
{
	device_for_each_child(lchip->dev, NULL, locomo_remove_child);
Linus Torvalds's avatar
Linus Torvalds committed
478 479

	if (lchip->irq != NO_IRQ) {
480
		irq_set_chained_handler_and_data(lchip->irq, NULL, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
481 482 483 484 485 486
	}

	iounmap(lchip->base);
	kfree(lchip);
}

487
static int locomo_probe(struct platform_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
488 489 490 491
{
	struct resource *mem;
	int irq;

492
	mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
Linus Torvalds's avatar
Linus Torvalds committed
493 494
	if (!mem)
		return -EINVAL;
495
	irq = platform_get_irq(dev, 0);
496 497
	if (irq < 0)
		return -ENXIO;
Linus Torvalds's avatar
Linus Torvalds committed
498

499
	return __locomo_probe(&dev->dev, mem, irq);
Linus Torvalds's avatar
Linus Torvalds committed
500 501
}

502
static int locomo_remove(struct platform_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
503
{
Pavel Machek's avatar
Pavel Machek committed
504
	struct locomo *lchip = platform_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
505 506 507

	if (lchip) {
		__locomo_remove(lchip);
508
		platform_set_drvdata(dev, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
509 510 511 512 513 514 515 516 517 518 519
	}

	return 0;
}

/*
 *	Not sure if this should be on the system bus or not yet.
 *	We really want some way to register a system device at
 *	the per-machine level, and then have this driver pick
 *	up the registered devices.
 */
520
static struct platform_driver locomo_device_driver = {
Linus Torvalds's avatar
Linus Torvalds committed
521 522
	.probe		= locomo_probe,
	.remove		= locomo_remove,
523 524 525 526
#ifdef CONFIG_PM
	.suspend	= locomo_suspend,
	.resume		= locomo_resume,
#endif
527 528 529
	.driver		= {
		.name	= "locomo",
	},
Linus Torvalds's avatar
Linus Torvalds committed
530 531 532 533 534 535 536 537 538 539 540
};

/*
 *	Get the parent device driver (us) structure
 *	from a child function device
 */
static inline struct locomo *locomo_chip_driver(struct locomo_dev *ldev)
{
	return (struct locomo *)dev_get_drvdata(ldev->dev.parent);
}

541
void locomo_gpio_set_dir(struct device *dev, unsigned int bits, unsigned int dir)
Linus Torvalds's avatar
Linus Torvalds committed
542
{
543
	struct locomo *lchip = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
544 545 546
	unsigned long flags;
	unsigned int r;

547 548 549
	if (!lchip)
		return;

Linus Torvalds's avatar
Linus Torvalds committed
550 551 552
	spin_lock_irqsave(&lchip->lock, flags);

	r = locomo_readl(lchip->base + LOCOMO_GPD);
553 554 555 556
	if (dir)
		r |= bits;
	else
		r &= ~bits;
Linus Torvalds's avatar
Linus Torvalds committed
557 558 559 560 561 562 563 564 565 566 567
	locomo_writel(r, lchip->base + LOCOMO_GPD);

	r = locomo_readl(lchip->base + LOCOMO_GPE);
	if (dir)
		r |= bits;
	else
		r &= ~bits;
	locomo_writel(r, lchip->base + LOCOMO_GPE);

	spin_unlock_irqrestore(&lchip->lock, flags);
}
568
EXPORT_SYMBOL(locomo_gpio_set_dir);
Linus Torvalds's avatar
Linus Torvalds committed
569

570
int locomo_gpio_read_level(struct device *dev, unsigned int bits)
Linus Torvalds's avatar
Linus Torvalds committed
571
{
572
	struct locomo *lchip = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
573 574 575
	unsigned long flags;
	unsigned int ret;

576 577 578
	if (!lchip)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
579 580 581 582 583 584 585
	spin_lock_irqsave(&lchip->lock, flags);
	ret = locomo_readl(lchip->base + LOCOMO_GPL);
	spin_unlock_irqrestore(&lchip->lock, flags);

	ret &= bits;
	return ret;
}
586
EXPORT_SYMBOL(locomo_gpio_read_level);
Linus Torvalds's avatar
Linus Torvalds committed
587

588
int locomo_gpio_read_output(struct device *dev, unsigned int bits)
Linus Torvalds's avatar
Linus Torvalds committed
589
{
590
	struct locomo *lchip = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
591 592 593
	unsigned long flags;
	unsigned int ret;

594 595 596
	if (!lchip)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
597 598 599 600 601 602 603
	spin_lock_irqsave(&lchip->lock, flags);
	ret = locomo_readl(lchip->base + LOCOMO_GPO);
	spin_unlock_irqrestore(&lchip->lock, flags);

	ret &= bits;
	return ret;
}
604
EXPORT_SYMBOL(locomo_gpio_read_output);
Linus Torvalds's avatar
Linus Torvalds committed
605

606
void locomo_gpio_write(struct device *dev, unsigned int bits, unsigned int set)
Linus Torvalds's avatar
Linus Torvalds committed
607
{
608
	struct locomo *lchip = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
609 610 611
	unsigned long flags;
	unsigned int r;

612 613 614
	if (!lchip)
		return;

Linus Torvalds's avatar
Linus Torvalds committed
615 616 617 618 619 620 621 622 623 624 625
	spin_lock_irqsave(&lchip->lock, flags);

	r = locomo_readl(lchip->base + LOCOMO_GPO);
	if (set)
		r |= bits;
	else
		r &= ~bits;
	locomo_writel(r, lchip->base + LOCOMO_GPO);

	spin_unlock_irqrestore(&lchip->lock, flags);
}
626
EXPORT_SYMBOL(locomo_gpio_write);
Linus Torvalds's avatar
Linus Torvalds committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

static void locomo_m62332_sendbit(void *mapbase, int bit)
{
	unsigned int r;

	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */

	if (bit & 1) {
		r = locomo_readl(mapbase + LOCOMO_DAC);
		r |=  LOCOMO_DAC_SDAOEB;
		locomo_writel(r, mapbase + LOCOMO_DAC);
		udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	} else {
		r = locomo_readl(mapbase + LOCOMO_DAC);
		r &=  ~(LOCOMO_DAC_SDAOEB);
		locomo_writel(r, mapbase + LOCOMO_DAC);
		udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	}

	udelay(DAC_DATA_SETUP_TIME);	/* 250 nsec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/*  4.0 usec */
}

void locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel)
{
	struct locomo *lchip = locomo_chip_driver(ldev);
	int i;
	unsigned char data;
	unsigned int r;
	void *mapbase = lchip->base;
	unsigned long flags;

	spin_lock_irqsave(&lchip->lock, flags);

	/* Start */
	udelay(DAC_BUS_FREE_TIME);	/* 5.0 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.0 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SDAOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_START_HOLD_TIME);	/* 5.0 usec */
	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */

	/* Send slave address and W bit (LSB is W bit) */
	data = (M62332_SLAVE_ADDR << 1) | M62332_W_BIT;
	for (i = 1; i <= 8; i++) {
		locomo_m62332_sendbit(mapbase, data >> (8 - i));
	}

	/* Check A bit */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SDAOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
		printk(KERN_WARNING "locomo: m62332_senddata Error 1\n");
710
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
	}

	/* Send Sub address (LSB is channel select) */
	/*    channel = 0 : ch1 select              */
	/*            = 1 : ch2 select              */
	data = M62332_SUB_ADDR + channel;
	for (i = 1; i <= 8; i++) {
		locomo_m62332_sendbit(mapbase, data >> (8 - i));
	}

	/* Check A bit */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SDAOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
		printk(KERN_WARNING "locomo: m62332_senddata Error 2\n");
738
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
	}

	/* Send DAC data */
	for (i = 1; i <= 8; i++) {
		locomo_m62332_sendbit(mapbase, dac_data >> (8 - i));
	}

	/* Check A bit */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SDAOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
		printk(KERN_WARNING "locomo: m62332_senddata Error 3\n");
	}

765
out:
Linus Torvalds's avatar
Linus Torvalds committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
	/* stop */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r &=  ~(LOCOMO_DAC_SCLOEB);
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */
	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SDAOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */

	r = locomo_readl(mapbase + LOCOMO_DAC);
	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
	locomo_writel(r, mapbase + LOCOMO_DAC);
	udelay(DAC_LOW_SETUP_TIME);	/* 1000 nsec */
	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */

	spin_unlock_irqrestore(&lchip->lock, flags);
}
791
EXPORT_SYMBOL(locomo_m62332_senddata);
Linus Torvalds's avatar
Linus Torvalds committed
792

793 794 795 796 797 798 799 800 801 802
/*
 *	Frontlight control
 */

void locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf)
{
	unsigned long flags;
	struct locomo *lchip = locomo_chip_driver(dev);

	if (vr)
803
		locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 1);
804
	else
805
		locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
806 807 808 809 810 811 812 813

	spin_lock_irqsave(&lchip->lock, flags);
	locomo_writel(bpwf, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
	udelay(100);
	locomo_writel(duty, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
	locomo_writel(bpwf | LOCOMO_ALC_EN, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
	spin_unlock_irqrestore(&lchip->lock, flags);
}
814
EXPORT_SYMBOL(locomo_frontlight_set);
815

Linus Torvalds's avatar
Linus Torvalds committed
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
/*
 *	LoCoMo "Register Access Bus."
 *
 *	We model this as a regular bus type, and hang devices directly
 *	off this.
 */
static int locomo_match(struct device *_dev, struct device_driver *_drv)
{
	struct locomo_dev *dev = LOCOMO_DEV(_dev);
	struct locomo_driver *drv = LOCOMO_DRV(_drv);

	return dev->devid == drv->devid;
}

static int locomo_bus_suspend(struct device *dev, pm_message_t state)
{
	struct locomo_dev *ldev = LOCOMO_DEV(dev);
	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
	int ret = 0;

	if (drv && drv->suspend)
		ret = drv->suspend(ldev, state);
	return ret;
}

static int locomo_bus_resume(struct device *dev)
{
	struct locomo_dev *ldev = LOCOMO_DEV(dev);
	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
	int ret = 0;

	if (drv && drv->resume)
		ret = drv->resume(ldev);
	return ret;
}

static int locomo_bus_probe(struct device *dev)
{
	struct locomo_dev *ldev = LOCOMO_DEV(dev);
	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
	int ret = -ENODEV;

	if (drv->probe)
		ret = drv->probe(ldev);
	return ret;
}

static int locomo_bus_remove(struct device *dev)
{
	struct locomo_dev *ldev = LOCOMO_DEV(dev);
	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
	int ret = 0;

	if (drv->remove)
		ret = drv->remove(ldev);
	return ret;
}

struct bus_type locomo_bus_type = {
	.name		= "locomo-bus",
	.match		= locomo_match,
877 878
	.probe		= locomo_bus_probe,
	.remove		= locomo_bus_remove,
Linus Torvalds's avatar
Linus Torvalds committed
879 880 881 882 883 884 885 886 887
	.suspend	= locomo_bus_suspend,
	.resume		= locomo_bus_resume,
};

int locomo_driver_register(struct locomo_driver *driver)
{
	driver->drv.bus = &locomo_bus_type;
	return driver_register(&driver->drv);
}
888
EXPORT_SYMBOL(locomo_driver_register);
Linus Torvalds's avatar
Linus Torvalds committed
889 890 891 892 893

void locomo_driver_unregister(struct locomo_driver *driver)
{
	driver_unregister(&driver->drv);
}
894
EXPORT_SYMBOL(locomo_driver_unregister);
Linus Torvalds's avatar
Linus Torvalds committed
895 896 897 898 899

static int __init locomo_init(void)
{
	int ret = bus_register(&locomo_bus_type);
	if (ret == 0)
900
		platform_driver_register(&locomo_device_driver);
Linus Torvalds's avatar
Linus Torvalds committed
901 902 903 904 905
	return ret;
}

static void __exit locomo_exit(void)
{
906
	platform_driver_unregister(&locomo_device_driver);
Linus Torvalds's avatar
Linus Torvalds committed
907 908 909 910 911 912 913 914 915
	bus_unregister(&locomo_bus_type);
}

module_init(locomo_init);
module_exit(locomo_exit);

MODULE_DESCRIPTION("Sharp LoCoMo core driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");