162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/common/locomo.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Sharp LoCoMo support 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file contains all generic LoCoMo support. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * All initialization functions provided here are intended to be called 1062306a36Sopenharmony_ci * from machine specific code with proper arguments when required. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Based on sa1111.c 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/ioport.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/irq.h> 2762306a36Sopenharmony_ci#include <asm/mach/irq.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/hardware/locomo.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* LoCoMo Interrupts */ 3262306a36Sopenharmony_ci#define IRQ_LOCOMO_KEY (0) 3362306a36Sopenharmony_ci#define IRQ_LOCOMO_GPIO (1) 3462306a36Sopenharmony_ci#define IRQ_LOCOMO_LT (2) 3562306a36Sopenharmony_ci#define IRQ_LOCOMO_SPI (3) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* M62332 output channel selection */ 3862306a36Sopenharmony_ci#define M62332_EVR_CH 1 /* M62332 volume channel number */ 3962306a36Sopenharmony_ci /* 0 : CH.1 , 1 : CH. 2 */ 4062306a36Sopenharmony_ci/* DAC send data */ 4162306a36Sopenharmony_ci#define M62332_SLAVE_ADDR 0x4e /* Slave address */ 4262306a36Sopenharmony_ci#define M62332_W_BIT 0x00 /* W bit (0 only) */ 4362306a36Sopenharmony_ci#define M62332_SUB_ADDR 0x00 /* Sub address */ 4462306a36Sopenharmony_ci#define M62332_A_BIT 0x00 /* A bit (0 only) */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* DAC setup and hold times (expressed in us) */ 4762306a36Sopenharmony_ci#define DAC_BUS_FREE_TIME 5 /* 4.7 us */ 4862306a36Sopenharmony_ci#define DAC_START_SETUP_TIME 5 /* 4.7 us */ 4962306a36Sopenharmony_ci#define DAC_STOP_SETUP_TIME 4 /* 4.0 us */ 5062306a36Sopenharmony_ci#define DAC_START_HOLD_TIME 5 /* 4.7 us */ 5162306a36Sopenharmony_ci#define DAC_SCL_LOW_HOLD_TIME 5 /* 4.7 us */ 5262306a36Sopenharmony_ci#define DAC_SCL_HIGH_HOLD_TIME 4 /* 4.0 us */ 5362306a36Sopenharmony_ci#define DAC_DATA_SETUP_TIME 1 /* 250 ns */ 5462306a36Sopenharmony_ci#define DAC_DATA_HOLD_TIME 1 /* 300 ns */ 5562306a36Sopenharmony_ci#define DAC_LOW_SETUP_TIME 1 /* 300 ns */ 5662306a36Sopenharmony_ci#define DAC_HIGH_SETUP_TIME 1 /* 1000 ns */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* the following is the overall data for the locomo chip */ 5962306a36Sopenharmony_cistruct locomo { 6062306a36Sopenharmony_ci struct device *dev; 6162306a36Sopenharmony_ci unsigned long phys; 6262306a36Sopenharmony_ci unsigned int irq; 6362306a36Sopenharmony_ci int irq_base; 6462306a36Sopenharmony_ci spinlock_t lock; 6562306a36Sopenharmony_ci void __iomem *base; 6662306a36Sopenharmony_ci#ifdef CONFIG_PM 6762306a36Sopenharmony_ci void *saved_state; 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct locomo_dev_info { 7262306a36Sopenharmony_ci unsigned long offset; 7362306a36Sopenharmony_ci unsigned long length; 7462306a36Sopenharmony_ci unsigned int devid; 7562306a36Sopenharmony_ci unsigned int irq[1]; 7662306a36Sopenharmony_ci const char * name; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* All the locomo devices. If offset is non-zero, the mapbase for the 8062306a36Sopenharmony_ci * locomo_dev will be set to the chip base plus offset. If offset is 8162306a36Sopenharmony_ci * zero, then the mapbase for the locomo_dev will be set to zero. An 8262306a36Sopenharmony_ci * offset of zero means the device only uses GPIOs or other helper 8362306a36Sopenharmony_ci * functions inside this file */ 8462306a36Sopenharmony_cistatic struct locomo_dev_info locomo_devices[] = { 8562306a36Sopenharmony_ci { 8662306a36Sopenharmony_ci .devid = LOCOMO_DEVID_KEYBOARD, 8762306a36Sopenharmony_ci .irq = { IRQ_LOCOMO_KEY }, 8862306a36Sopenharmony_ci .name = "locomo-keyboard", 8962306a36Sopenharmony_ci .offset = LOCOMO_KEYBOARD, 9062306a36Sopenharmony_ci .length = 16, 9162306a36Sopenharmony_ci }, 9262306a36Sopenharmony_ci { 9362306a36Sopenharmony_ci .devid = LOCOMO_DEVID_FRONTLIGHT, 9462306a36Sopenharmony_ci .irq = {}, 9562306a36Sopenharmony_ci .name = "locomo-frontlight", 9662306a36Sopenharmony_ci .offset = LOCOMO_FRONTLIGHT, 9762306a36Sopenharmony_ci .length = 8, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci { 10162306a36Sopenharmony_ci .devid = LOCOMO_DEVID_BACKLIGHT, 10262306a36Sopenharmony_ci .irq = {}, 10362306a36Sopenharmony_ci .name = "locomo-backlight", 10462306a36Sopenharmony_ci .offset = LOCOMO_BACKLIGHT, 10562306a36Sopenharmony_ci .length = 8, 10662306a36Sopenharmony_ci }, 10762306a36Sopenharmony_ci { 10862306a36Sopenharmony_ci .devid = LOCOMO_DEVID_AUDIO, 10962306a36Sopenharmony_ci .irq = {}, 11062306a36Sopenharmony_ci .name = "locomo-audio", 11162306a36Sopenharmony_ci .offset = LOCOMO_AUDIO, 11262306a36Sopenharmony_ci .length = 4, 11362306a36Sopenharmony_ci }, 11462306a36Sopenharmony_ci { 11562306a36Sopenharmony_ci .devid = LOCOMO_DEVID_LED, 11662306a36Sopenharmony_ci .irq = {}, 11762306a36Sopenharmony_ci .name = "locomo-led", 11862306a36Sopenharmony_ci .offset = LOCOMO_LED, 11962306a36Sopenharmony_ci .length = 8, 12062306a36Sopenharmony_ci }, 12162306a36Sopenharmony_ci { 12262306a36Sopenharmony_ci .devid = LOCOMO_DEVID_UART, 12362306a36Sopenharmony_ci .irq = {}, 12462306a36Sopenharmony_ci .name = "locomo-uart", 12562306a36Sopenharmony_ci .offset = 0, 12662306a36Sopenharmony_ci .length = 0, 12762306a36Sopenharmony_ci }, 12862306a36Sopenharmony_ci { 12962306a36Sopenharmony_ci .devid = LOCOMO_DEVID_SPI, 13062306a36Sopenharmony_ci .irq = {}, 13162306a36Sopenharmony_ci .name = "locomo-spi", 13262306a36Sopenharmony_ci .offset = LOCOMO_SPI, 13362306a36Sopenharmony_ci .length = 0x30, 13462306a36Sopenharmony_ci }, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void locomo_handler(struct irq_desc *desc) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct locomo *lchip = irq_desc_get_handler_data(desc); 14062306a36Sopenharmony_ci int req, i; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Acknowledge the parent IRQ */ 14362306a36Sopenharmony_ci desc->irq_data.chip->irq_ack(&desc->irq_data); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* check why this interrupt was generated */ 14662306a36Sopenharmony_ci req = locomo_readl(lchip->base + LOCOMO_ICR) & 0x0f00; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (req) { 14962306a36Sopenharmony_ci unsigned int irq; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* generate the next interrupt(s) */ 15262306a36Sopenharmony_ci irq = lchip->irq_base; 15362306a36Sopenharmony_ci for (i = 0; i <= 3; i++, irq++) { 15462306a36Sopenharmony_ci if (req & (0x0100 << i)) { 15562306a36Sopenharmony_ci generic_handle_irq(irq); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void locomo_ack_irq(struct irq_data *d) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void locomo_mask_irq(struct irq_data *d) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct locomo *lchip = irq_data_get_irq_chip_data(d); 16962306a36Sopenharmony_ci unsigned int r; 17062306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_ICR); 17162306a36Sopenharmony_ci r &= ~(0x0010 << (d->irq - lchip->irq_base)); 17262306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_ICR); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void locomo_unmask_irq(struct irq_data *d) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct locomo *lchip = irq_data_get_irq_chip_data(d); 17862306a36Sopenharmony_ci unsigned int r; 17962306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_ICR); 18062306a36Sopenharmony_ci r |= (0x0010 << (d->irq - lchip->irq_base)); 18162306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_ICR); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct irq_chip locomo_chip = { 18562306a36Sopenharmony_ci .name = "LOCOMO", 18662306a36Sopenharmony_ci .irq_ack = locomo_ack_irq, 18762306a36Sopenharmony_ci .irq_mask = locomo_mask_irq, 18862306a36Sopenharmony_ci .irq_unmask = locomo_unmask_irq, 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void locomo_setup_irq(struct locomo *lchip) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int irq = lchip->irq_base; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Install handler for IRQ_LOCOMO_HW. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci irq_set_irq_type(lchip->irq, IRQ_TYPE_EDGE_FALLING); 19962306a36Sopenharmony_ci irq_set_chained_handler_and_data(lchip->irq, locomo_handler, lchip); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Install handlers for IRQ_LOCOMO_* */ 20262306a36Sopenharmony_ci for ( ; irq <= lchip->irq_base + 3; irq++) { 20362306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &locomo_chip, handle_level_irq); 20462306a36Sopenharmony_ci irq_set_chip_data(irq, lchip); 20562306a36Sopenharmony_ci irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void locomo_dev_release(struct device *_dev) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct locomo_dev *dev = LOCOMO_DEV(_dev); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci kfree(dev); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int 21862306a36Sopenharmony_cilocomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct locomo_dev *dev; 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci dev = kzalloc(sizeof(struct locomo_dev), GFP_KERNEL); 22462306a36Sopenharmony_ci if (!dev) { 22562306a36Sopenharmony_ci ret = -ENOMEM; 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * If the parent device has a DMA mask associated with it, 23162306a36Sopenharmony_ci * propagate it down to the children. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (lchip->dev->dma_mask) { 23462306a36Sopenharmony_ci dev->dma_mask = *lchip->dev->dma_mask; 23562306a36Sopenharmony_ci dev->dev.dma_mask = &dev->dma_mask; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev_set_name(&dev->dev, "%s", info->name); 23962306a36Sopenharmony_ci dev->devid = info->devid; 24062306a36Sopenharmony_ci dev->dev.parent = lchip->dev; 24162306a36Sopenharmony_ci dev->dev.bus = &locomo_bus_type; 24262306a36Sopenharmony_ci dev->dev.release = locomo_dev_release; 24362306a36Sopenharmony_ci dev->dev.coherent_dma_mask = lchip->dev->coherent_dma_mask; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (info->offset) 24662306a36Sopenharmony_ci dev->mapbase = lchip->base + info->offset; 24762306a36Sopenharmony_ci else 24862306a36Sopenharmony_ci dev->mapbase = 0; 24962306a36Sopenharmony_ci dev->length = info->length; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dev->irq[0] = (lchip->irq_base == NO_IRQ) ? 25262306a36Sopenharmony_ci NO_IRQ : lchip->irq_base + info->irq[0]; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = device_register(&dev->dev); 25562306a36Sopenharmony_ci if (ret) { 25662306a36Sopenharmony_ci out: 25762306a36Sopenharmony_ci kfree(dev); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci#ifdef CONFIG_PM 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistruct locomo_save_data { 26562306a36Sopenharmony_ci u16 LCM_GPO; 26662306a36Sopenharmony_ci u16 LCM_SPICT; 26762306a36Sopenharmony_ci u16 LCM_GPE; 26862306a36Sopenharmony_ci u16 LCM_ASD; 26962306a36Sopenharmony_ci u16 LCM_SPIMD; 27062306a36Sopenharmony_ci}; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int locomo_suspend(struct platform_device *dev, pm_message_t state) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct locomo *lchip = platform_get_drvdata(dev); 27562306a36Sopenharmony_ci struct locomo_save_data *save; 27662306a36Sopenharmony_ci unsigned long flags; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL); 27962306a36Sopenharmony_ci if (!save) 28062306a36Sopenharmony_ci return -ENOMEM; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci lchip->saved_state = save; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci save->LCM_GPO = locomo_readl(lchip->base + LOCOMO_GPO); /* GPIO */ 28762306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_GPO); 28862306a36Sopenharmony_ci save->LCM_SPICT = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPICT); /* SPI */ 28962306a36Sopenharmony_ci locomo_writel(0x40, lchip->base + LOCOMO_SPI + LOCOMO_SPICT); 29062306a36Sopenharmony_ci save->LCM_GPE = locomo_readl(lchip->base + LOCOMO_GPE); /* GPIO */ 29162306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_GPE); 29262306a36Sopenharmony_ci save->LCM_ASD = locomo_readl(lchip->base + LOCOMO_ASD); /* ADSTART */ 29362306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_ASD); 29462306a36Sopenharmony_ci save->LCM_SPIMD = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPIMD); /* SPI */ 29562306a36Sopenharmony_ci locomo_writel(0x3C14, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_PAIF); 29862306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_DAC); 29962306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_BACKLIGHT + LOCOMO_TC); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if ((locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT0) & 0x88) && (locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT1) & 0x88)) 30262306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_C32K); /* CLK32 off */ 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci /* 18MHz already enabled, so no wait */ 30562306a36Sopenharmony_ci locomo_writel(0xc1, lchip->base + LOCOMO_C32K); /* CLK32 on */ 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_TADC); /* 18MHz clock off*/ 30862306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_AUDIO + LOCOMO_ACC); /* 22MHz/24MHz clock off */ 30962306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS); /* FL */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int locomo_resume(struct platform_device *dev) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct locomo *lchip = platform_get_drvdata(dev); 31962306a36Sopenharmony_ci struct locomo_save_data *save; 32062306a36Sopenharmony_ci unsigned long r; 32162306a36Sopenharmony_ci unsigned long flags; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci save = lchip->saved_state; 32462306a36Sopenharmony_ci if (!save) 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci locomo_writel(save->LCM_GPO, lchip->base + LOCOMO_GPO); 33062306a36Sopenharmony_ci locomo_writel(save->LCM_SPICT, lchip->base + LOCOMO_SPI + LOCOMO_SPICT); 33162306a36Sopenharmony_ci locomo_writel(save->LCM_GPE, lchip->base + LOCOMO_GPE); 33262306a36Sopenharmony_ci locomo_writel(save->LCM_ASD, lchip->base + LOCOMO_ASD); 33362306a36Sopenharmony_ci locomo_writel(save->LCM_SPIMD, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci locomo_writel(0x00, lchip->base + LOCOMO_C32K); 33662306a36Sopenharmony_ci locomo_writel(0x90, lchip->base + LOCOMO_TADC); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KSC); 33962306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC); 34062306a36Sopenharmony_ci r &= 0xFEFF; 34162306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC); 34262306a36Sopenharmony_ci locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci lchip->saved_state = NULL; 34762306a36Sopenharmony_ci kfree(save); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci#endif 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int 35462306a36Sopenharmony_ci__locomo_probe(struct device *me, struct resource *mem, int irq) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct locomo_platform_data *pdata = me->platform_data; 35762306a36Sopenharmony_ci struct locomo *lchip; 35862306a36Sopenharmony_ci unsigned long r; 35962306a36Sopenharmony_ci int i, ret = -ENODEV; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci lchip = kzalloc(sizeof(struct locomo), GFP_KERNEL); 36262306a36Sopenharmony_ci if (!lchip) 36362306a36Sopenharmony_ci return -ENOMEM; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock_init(&lchip->lock); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci lchip->dev = me; 36862306a36Sopenharmony_ci dev_set_drvdata(lchip->dev, lchip); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci lchip->phys = mem->start; 37162306a36Sopenharmony_ci lchip->irq = irq; 37262306a36Sopenharmony_ci lchip->irq_base = (pdata) ? pdata->irq_base : NO_IRQ; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * Map the whole region. This also maps the 37662306a36Sopenharmony_ci * registers for our children. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci lchip->base = ioremap(mem->start, PAGE_SIZE); 37962306a36Sopenharmony_ci if (!lchip->base) { 38062306a36Sopenharmony_ci ret = -ENOMEM; 38162306a36Sopenharmony_ci goto out; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* locomo initialize */ 38562306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_ICR); 38662306a36Sopenharmony_ci /* KEYBOARD */ 38762306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* GPIO */ 39062306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_GPO); 39162306a36Sopenharmony_ci locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14)) 39262306a36Sopenharmony_ci , lchip->base + LOCOMO_GPE); 39362306a36Sopenharmony_ci locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14)) 39462306a36Sopenharmony_ci , lchip->base + LOCOMO_GPD); 39562306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_GIE); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Frontlight */ 39862306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS); 39962306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Longtime timer */ 40262306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_LTINT); 40362306a36Sopenharmony_ci /* SPI */ 40462306a36Sopenharmony_ci locomo_writel(0, lchip->base + LOCOMO_SPI + LOCOMO_SPIIE); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci locomo_writel(6 + 8 + 320 + 30 - 10, lchip->base + LOCOMO_ASD); 40762306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_ASD); 40862306a36Sopenharmony_ci r |= 0x8000; 40962306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_ASD); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci locomo_writel(6 + 8 + 320 + 30 - 10 - 128 + 4, lchip->base + LOCOMO_HSD); 41262306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_HSD); 41362306a36Sopenharmony_ci r |= 0x8000; 41462306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_HSD); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci locomo_writel(128 / 8, lchip->base + LOCOMO_HSC); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* XON */ 41962306a36Sopenharmony_ci locomo_writel(0x80, lchip->base + LOCOMO_TADC); 42062306a36Sopenharmony_ci udelay(1000); 42162306a36Sopenharmony_ci /* CLK9MEN */ 42262306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_TADC); 42362306a36Sopenharmony_ci r |= 0x10; 42462306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_TADC); 42562306a36Sopenharmony_ci udelay(100); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* init DAC */ 42862306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_DAC); 42962306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB; 43062306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_DAC); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_VER); 43362306a36Sopenharmony_ci printk(KERN_INFO "LoCoMo Chip: %lu%lu\n", (r >> 8), (r & 0xff)); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * The interrupt controller must be initialised before any 43762306a36Sopenharmony_ci * other device to ensure that the interrupts are available. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci if (lchip->irq != NO_IRQ && lchip->irq_base != NO_IRQ) 44062306a36Sopenharmony_ci locomo_setup_irq(lchip); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(locomo_devices); i++) 44362306a36Sopenharmony_ci locomo_init_one_child(lchip, &locomo_devices[i]); 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci out: 44762306a36Sopenharmony_ci kfree(lchip); 44862306a36Sopenharmony_ci return ret; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int locomo_remove_child(struct device *dev, void *data) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci device_unregister(dev); 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void __locomo_remove(struct locomo *lchip) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci device_for_each_child(lchip->dev, NULL, locomo_remove_child); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (lchip->irq != NO_IRQ) { 46262306a36Sopenharmony_ci irq_set_chained_handler_and_data(lchip->irq, NULL, NULL); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci iounmap(lchip->base); 46662306a36Sopenharmony_ci kfree(lchip); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/** 47062306a36Sopenharmony_ci * locomo_probe - probe for a single LoCoMo chip. 47162306a36Sopenharmony_ci * @dev: platform device 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * Probe for a LoCoMo chip. This must be called 47462306a36Sopenharmony_ci * before any other locomo-specific code. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * Returns: 47762306a36Sopenharmony_ci * * %-EINVAL - device's IORESOURCE_MEM not found 47862306a36Sopenharmony_ci * * %-ENXIO - could not allocate an IRQ for the device 47962306a36Sopenharmony_ci * * %-ENODEV - device not found. 48062306a36Sopenharmony_ci * * %-EBUSY - physical address already marked in-use. 48162306a36Sopenharmony_ci * * %-ENOMEM - could not allocate or iomap memory. 48262306a36Sopenharmony_ci * * %0 - successful. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_cistatic int locomo_probe(struct platform_device *dev) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct resource *mem; 48762306a36Sopenharmony_ci int irq; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci mem = platform_get_resource(dev, IORESOURCE_MEM, 0); 49062306a36Sopenharmony_ci if (!mem) 49162306a36Sopenharmony_ci return -EINVAL; 49262306a36Sopenharmony_ci irq = platform_get_irq(dev, 0); 49362306a36Sopenharmony_ci if (irq < 0) 49462306a36Sopenharmony_ci return -ENXIO; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return __locomo_probe(&dev->dev, mem, irq); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void locomo_remove(struct platform_device *dev) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct locomo *lchip = platform_get_drvdata(dev); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (lchip) { 50462306a36Sopenharmony_ci __locomo_remove(lchip); 50562306a36Sopenharmony_ci platform_set_drvdata(dev, NULL); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/* 51062306a36Sopenharmony_ci * Not sure if this should be on the system bus or not yet. 51162306a36Sopenharmony_ci * We really want some way to register a system device at 51262306a36Sopenharmony_ci * the per-machine level, and then have this driver pick 51362306a36Sopenharmony_ci * up the registered devices. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_cistatic struct platform_driver locomo_device_driver = { 51662306a36Sopenharmony_ci .probe = locomo_probe, 51762306a36Sopenharmony_ci .remove_new = locomo_remove, 51862306a36Sopenharmony_ci#ifdef CONFIG_PM 51962306a36Sopenharmony_ci .suspend = locomo_suspend, 52062306a36Sopenharmony_ci .resume = locomo_resume, 52162306a36Sopenharmony_ci#endif 52262306a36Sopenharmony_ci .driver = { 52362306a36Sopenharmony_ci .name = "locomo", 52462306a36Sopenharmony_ci }, 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* 52862306a36Sopenharmony_ci * Get the parent device driver (us) structure 52962306a36Sopenharmony_ci * from a child function device 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic inline struct locomo *locomo_chip_driver(struct locomo_dev *ldev) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci return (struct locomo *)dev_get_drvdata(ldev->dev.parent); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_civoid locomo_gpio_set_dir(struct device *dev, unsigned int bits, unsigned int dir) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct locomo *lchip = dev_get_drvdata(dev); 53962306a36Sopenharmony_ci unsigned long flags; 54062306a36Sopenharmony_ci unsigned int r; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (!lchip) 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_GPD); 54862306a36Sopenharmony_ci if (dir) 54962306a36Sopenharmony_ci r |= bits; 55062306a36Sopenharmony_ci else 55162306a36Sopenharmony_ci r &= ~bits; 55262306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_GPD); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_GPE); 55562306a36Sopenharmony_ci if (dir) 55662306a36Sopenharmony_ci r |= bits; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci r &= ~bits; 55962306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_GPE); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_set_dir); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciint locomo_gpio_read_level(struct device *dev, unsigned int bits) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct locomo *lchip = dev_get_drvdata(dev); 56862306a36Sopenharmony_ci unsigned long flags; 56962306a36Sopenharmony_ci unsigned int ret; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!lchip) 57262306a36Sopenharmony_ci return -ENODEV; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 57562306a36Sopenharmony_ci ret = locomo_readl(lchip->base + LOCOMO_GPL); 57662306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci ret &= bits; 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_read_level); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciint locomo_gpio_read_output(struct device *dev, unsigned int bits) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct locomo *lchip = dev_get_drvdata(dev); 58662306a36Sopenharmony_ci unsigned long flags; 58762306a36Sopenharmony_ci unsigned int ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!lchip) 59062306a36Sopenharmony_ci return -ENODEV; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 59362306a36Sopenharmony_ci ret = locomo_readl(lchip->base + LOCOMO_GPO); 59462306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret &= bits; 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_read_output); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_civoid locomo_gpio_write(struct device *dev, unsigned int bits, unsigned int set) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct locomo *lchip = dev_get_drvdata(dev); 60462306a36Sopenharmony_ci unsigned long flags; 60562306a36Sopenharmony_ci unsigned int r; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!lchip) 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci r = locomo_readl(lchip->base + LOCOMO_GPO); 61362306a36Sopenharmony_ci if (set) 61462306a36Sopenharmony_ci r |= bits; 61562306a36Sopenharmony_ci else 61662306a36Sopenharmony_ci r &= ~bits; 61762306a36Sopenharmony_ci locomo_writel(r, lchip->base + LOCOMO_GPO); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_gpio_write); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void locomo_m62332_sendbit(void *mapbase, int bit) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci unsigned int r; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 62862306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 62962306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 63062306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 63162306a36Sopenharmony_ci udelay(DAC_DATA_HOLD_TIME); /* 300 nsec */ 63262306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 63362306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 63462306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 63562306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 63662306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (bit & 1) { 63962306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 64062306a36Sopenharmony_ci r |= LOCOMO_DAC_SDAOEB; 64162306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 64262306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 64362306a36Sopenharmony_ci } else { 64462306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 64562306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SDAOEB); 64662306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 64762306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci udelay(DAC_DATA_SETUP_TIME); /* 250 nsec */ 65162306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 65262306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB; 65362306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 65462306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 65562306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4.0 usec */ 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_civoid locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct locomo *lchip = locomo_chip_driver(ldev); 66162306a36Sopenharmony_ci int i; 66262306a36Sopenharmony_ci unsigned char data; 66362306a36Sopenharmony_ci unsigned int r; 66462306a36Sopenharmony_ci void *mapbase = lchip->base; 66562306a36Sopenharmony_ci unsigned long flags; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Start */ 67062306a36Sopenharmony_ci udelay(DAC_BUS_FREE_TIME); /* 5.0 usec */ 67162306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 67262306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB; 67362306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 67462306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 67562306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4.0 usec */ 67662306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 67762306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SDAOEB); 67862306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 67962306a36Sopenharmony_ci udelay(DAC_START_HOLD_TIME); /* 5.0 usec */ 68062306a36Sopenharmony_ci udelay(DAC_DATA_HOLD_TIME); /* 300 nsec */ 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Send slave address and W bit (LSB is W bit) */ 68362306a36Sopenharmony_ci data = (M62332_SLAVE_ADDR << 1) | M62332_W_BIT; 68462306a36Sopenharmony_ci for (i = 1; i <= 8; i++) { 68562306a36Sopenharmony_ci locomo_m62332_sendbit(mapbase, data >> (8 - i)); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* Check A bit */ 68962306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 69062306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 69162306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 69262306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 69362306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 69462306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 69562306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SDAOEB); 69662306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 69762306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 69862306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 69962306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB; 70062306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 70162306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 70262306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4.7 usec */ 70362306a36Sopenharmony_ci if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) { /* High is error */ 70462306a36Sopenharmony_ci printk(KERN_WARNING "locomo: m62332_senddata Error 1\n"); 70562306a36Sopenharmony_ci goto out; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Send Sub address (LSB is channel select) */ 70962306a36Sopenharmony_ci /* channel = 0 : ch1 select */ 71062306a36Sopenharmony_ci /* = 1 : ch2 select */ 71162306a36Sopenharmony_ci data = M62332_SUB_ADDR + channel; 71262306a36Sopenharmony_ci for (i = 1; i <= 8; i++) { 71362306a36Sopenharmony_ci locomo_m62332_sendbit(mapbase, data >> (8 - i)); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Check A bit */ 71762306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 71862306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 71962306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 72062306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 72162306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 72262306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 72362306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SDAOEB); 72462306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 72562306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 72662306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 72762306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB; 72862306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 72962306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 73062306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4.7 usec */ 73162306a36Sopenharmony_ci if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) { /* High is error */ 73262306a36Sopenharmony_ci printk(KERN_WARNING "locomo: m62332_senddata Error 2\n"); 73362306a36Sopenharmony_ci goto out; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Send DAC data */ 73762306a36Sopenharmony_ci for (i = 1; i <= 8; i++) { 73862306a36Sopenharmony_ci locomo_m62332_sendbit(mapbase, dac_data >> (8 - i)); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Check A bit */ 74262306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 74362306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 74462306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 74562306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 74662306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 74762306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 74862306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SDAOEB); 74962306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 75062306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 75162306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 75262306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB; 75362306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 75462306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 75562306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4.7 usec */ 75662306a36Sopenharmony_ci if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) { /* High is error */ 75762306a36Sopenharmony_ci printk(KERN_WARNING "locomo: m62332_senddata Error 3\n"); 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ciout: 76162306a36Sopenharmony_ci /* stop */ 76262306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 76362306a36Sopenharmony_ci r &= ~(LOCOMO_DAC_SCLOEB); 76462306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 76562306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 300 nsec */ 76662306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 76762306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 76862306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB; 76962306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 77062306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 77162306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4 usec */ 77262306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 77362306a36Sopenharmony_ci r |= LOCOMO_DAC_SDAOEB; 77462306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 77562306a36Sopenharmony_ci udelay(DAC_HIGH_SETUP_TIME); /* 1000 nsec */ 77662306a36Sopenharmony_ci udelay(DAC_SCL_HIGH_HOLD_TIME); /* 4 usec */ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci r = locomo_readl(mapbase + LOCOMO_DAC); 77962306a36Sopenharmony_ci r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB; 78062306a36Sopenharmony_ci locomo_writel(r, mapbase + LOCOMO_DAC); 78162306a36Sopenharmony_ci udelay(DAC_LOW_SETUP_TIME); /* 1000 nsec */ 78262306a36Sopenharmony_ci udelay(DAC_SCL_LOW_HOLD_TIME); /* 4.7 usec */ 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_m62332_senddata); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/* 78962306a36Sopenharmony_ci * Frontlight control 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_civoid locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci unsigned long flags; 79562306a36Sopenharmony_ci struct locomo *lchip = locomo_chip_driver(dev); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (vr) 79862306a36Sopenharmony_ci locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 1); 79962306a36Sopenharmony_ci else 80062306a36Sopenharmony_ci locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 0); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci spin_lock_irqsave(&lchip->lock, flags); 80362306a36Sopenharmony_ci locomo_writel(bpwf, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS); 80462306a36Sopenharmony_ci udelay(100); 80562306a36Sopenharmony_ci locomo_writel(duty, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD); 80662306a36Sopenharmony_ci locomo_writel(bpwf | LOCOMO_ALC_EN, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS); 80762306a36Sopenharmony_ci spin_unlock_irqrestore(&lchip->lock, flags); 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_frontlight_set); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci/* 81262306a36Sopenharmony_ci * LoCoMo "Register Access Bus." 81362306a36Sopenharmony_ci * 81462306a36Sopenharmony_ci * We model this as a regular bus type, and hang devices directly 81562306a36Sopenharmony_ci * off this. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_cistatic int locomo_match(struct device *_dev, struct device_driver *_drv) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct locomo_dev *dev = LOCOMO_DEV(_dev); 82062306a36Sopenharmony_ci struct locomo_driver *drv = LOCOMO_DRV(_drv); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return dev->devid == drv->devid; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int locomo_bus_probe(struct device *dev) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct locomo_dev *ldev = LOCOMO_DEV(dev); 82862306a36Sopenharmony_ci struct locomo_driver *drv = LOCOMO_DRV(dev->driver); 82962306a36Sopenharmony_ci int ret = -ENODEV; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (drv->probe) 83262306a36Sopenharmony_ci ret = drv->probe(ldev); 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void locomo_bus_remove(struct device *dev) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct locomo_dev *ldev = LOCOMO_DEV(dev); 83962306a36Sopenharmony_ci struct locomo_driver *drv = LOCOMO_DRV(dev->driver); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (drv->remove) 84262306a36Sopenharmony_ci drv->remove(ldev); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistruct bus_type locomo_bus_type = { 84662306a36Sopenharmony_ci .name = "locomo-bus", 84762306a36Sopenharmony_ci .match = locomo_match, 84862306a36Sopenharmony_ci .probe = locomo_bus_probe, 84962306a36Sopenharmony_ci .remove = locomo_bus_remove, 85062306a36Sopenharmony_ci}; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ciint locomo_driver_register(struct locomo_driver *driver) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci driver->drv.bus = &locomo_bus_type; 85562306a36Sopenharmony_ci return driver_register(&driver->drv); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_driver_register); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_civoid locomo_driver_unregister(struct locomo_driver *driver) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci driver_unregister(&driver->drv); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ciEXPORT_SYMBOL(locomo_driver_unregister); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int __init locomo_init(void) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci int ret = bus_register(&locomo_bus_type); 86862306a36Sopenharmony_ci if (ret == 0) 86962306a36Sopenharmony_ci platform_driver_register(&locomo_device_driver); 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void __exit locomo_exit(void) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci platform_driver_unregister(&locomo_device_driver); 87662306a36Sopenharmony_ci bus_unregister(&locomo_bus_type); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cimodule_init(locomo_init); 88062306a36Sopenharmony_cimodule_exit(locomo_exit); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ciMODULE_DESCRIPTION("Sharp LoCoMo core driver"); 88362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 88462306a36Sopenharmony_ciMODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>"); 885