aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-handheld-4.0/locomo
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/linux-handheld-4.0/locomo')
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0001-iio-add-m62332-DAC-driver.patch295
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0002-mfd-add-new-driver-for-Sharp-LoCoMo.patch603
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0003-leds-port-locomo-leds-driver-to-new-locomo-core.patch187
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0004-input-convert-LoCoMo-keyboard-driver-to-use-new-loco.patch480
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0005-input-locomokbd-provide-an-Alt-SysRQ-combination.patch75
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0006-video-backlight-add-new-locomo-backlight-driver.patch212
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0007-video-lcd-add-LoCoMo-LCD-driver.patch343
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0008-GPIO-port-LoCoMo-gpio-support-from-old-driver.patch226
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0009-gpio-locomo-implement-per-pin-irq-handling.patch204
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0010-spi-add-locomo-SPI-driver.patch421
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0011-i2c-add-locomo-i2c-driver.patch196
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0012-ARM-drop-old-LoCoMo-driver.patch1194
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0013-video-backlight-drop-old-locomo-bl-lcd-driver.patch278
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0014-ARM-sa1100-make-collie-use-new-locomo-drivers.patch408
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0015-ARM-sa1100-don-t-preallocate-IRQ-space-for-locomo.patch45
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0016-ARM-pxa-poodle-use-new-LoCoMo-driver.patch174
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0017-ARM-pxa-poodle-don-t-preallocate-IRQ-space-for-locom.patch30
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0018-ASoC-pxa-poodle-make-use-of-new-locomo-GPIO-interfac.patch120
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0019-poodle-gpio-uart-hack.patch26
-rw-r--r--recipes-kernel/linux/linux-handheld-4.0/locomo/0020-irq-HACK-around-for-handling-irq0-when-we-should.patch29
20 files changed, 5546 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0001-iio-add-m62332-DAC-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0001-iio-add-m62332-DAC-driver.patch
new file mode 100644
index 0000000..d1cf814
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0001-iio-add-m62332-DAC-driver.patch
@@ -0,0 +1,295 @@
+From 879c0ec1131e472f752d70ddf117dcabaf8ea106 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Sun, 23 Nov 2014 18:42:52 +0300
+Subject: [PATCH 01/20] iio: add m62332 DAC driver
+
+m62332 is a simple 2-channel DAC used on several Sharp Zaurus boards to
+control LCD voltage, backlight and sound. The driver use regulators to
+control the reference voltage and enabling/disabling the DAC.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/iio/dac/Kconfig | 10 ++
+ drivers/iio/dac/Makefile | 1 +
+ drivers/iio/dac/m62332.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 247 insertions(+)
+ create mode 100644 drivers/iio/dac/m62332.c
+
+diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
+index 2236ea2..2ede061 100644
+--- a/drivers/iio/dac/Kconfig
++++ b/drivers/iio/dac/Kconfig
+@@ -142,6 +142,16 @@ config AD7303
+ To compile this driver as module choose M here: the module will be called
+ ad7303.
+
++config M62332
++ tristate "Mitsubishi M62332 DAC driver"
++ depends on I2C
++ help
++ If you say yes here you get support for the Mitsubishi M62332
++ (I2C 8-Bit DACs with rail-to-rail outputs).
++
++ This driver can also be built as a module. If so, the module
++ will be called m62332.
++
+ config MAX517
+ tristate "Maxim MAX517/518/519 DAC driver"
+ depends on I2C
+diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
+index 52be7e1..63ae056 100644
+--- a/drivers/iio/dac/Makefile
++++ b/drivers/iio/dac/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
+ obj-$(CONFIG_AD5791) += ad5791.o
+ obj-$(CONFIG_AD5686) += ad5686.o
+ obj-$(CONFIG_AD7303) += ad7303.o
++obj-$(CONFIG_M62332) += m62332.o
+ obj-$(CONFIG_MAX517) += max517.o
+ obj-$(CONFIG_MAX5821) += max5821.o
+ obj-$(CONFIG_MCP4725) += mcp4725.o
+diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
+new file mode 100644
+index 0000000..90574d7
+--- /dev/null
++++ b/drivers/iio/dac/m62332.c
+@@ -0,0 +1,236 @@
++/*
++ * m62332.c - Support for Mitsubishi m62332 DAC
++ *
++ * Copyright (c) 2014 Dmitry Eremin-Solenikov
++ *
++ * Based on max517 driver:
++ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/jiffies.h>
++#include <linux/i2c.h>
++#include <linux/err.h>
++
++#include <linux/iio/iio.h>
++#include <linux/iio/driver.h>
++
++#include <linux/regulator/consumer.h>
++
++struct m62332_data {
++ struct i2c_client *client;
++ unsigned short vref_mv;
++ unsigned char raw[2];
++ struct regulator *vcc;
++#ifdef CONFIG_PM_SLEEP
++ unsigned char save[2];
++#endif
++};
++
++static int m62332_set_value(struct iio_dev *indio_dev,
++ unsigned char val, int channel)
++{
++ struct m62332_data *data = iio_priv(indio_dev);
++ struct i2c_client *client = data->client;
++ u8 outbuf[2];
++ int res = 0;
++
++ if (val == data->raw[channel])
++ return 0;
++
++ outbuf[0] = channel;
++ outbuf[1] = val;
++
++ if (val || data->raw[!channel])
++ res = regulator_enable(data->vcc);
++ if (res)
++ return res;
++
++ res = i2c_master_send(client, outbuf, 2);
++ if (res < 0)
++ return res;
++ else if (res != 2)
++ return -EIO;
++
++ data->raw[channel] = val;
++
++ if (!data->raw[0] && !data->raw[1])
++ regulator_disable(data->vcc);
++
++ return 0;
++}
++
++static int m62332_read_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan,
++ int *val,
++ int *val2,
++ long m)
++{
++ struct m62332_data *data = iio_priv(indio_dev);
++
++ switch (m) {
++ case IIO_CHAN_INFO_SCALE:
++ /* Corresponds to Vref / 2^(bits) */
++ *val = data->vref_mv;
++ *val2 = 8;
++ return IIO_VAL_FRACTIONAL_LOG2;
++ case IIO_CHAN_INFO_RAW:
++ *val = data->raw[chan->channel];
++ return IIO_VAL_INT;
++ default:
++ break;
++ }
++ return -EINVAL;
++}
++
++static int m62332_write_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan, int val, int val2, long mask)
++{
++ int ret;
++
++ switch (mask) {
++ case IIO_CHAN_INFO_RAW:
++ if (val < 0 || val > 255)
++ return -EINVAL;
++
++ ret = m62332_set_value(indio_dev, val, chan->channel);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int m62332_suspend(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct iio_dev *indio_dev = i2c_get_clientdata(client);
++ struct m62332_data *data = iio_priv(indio_dev);
++ int ret;
++
++ data->save[0] = data->raw[0];
++ data->save[1] = data->raw[1];
++
++ ret = m62332_set_value(indio_dev, 0, 0);
++ if (ret < 0)
++ return ret;
++
++ return m62332_set_value(indio_dev, 0, 1);
++}
++
++static int m62332_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct iio_dev *indio_dev = i2c_get_clientdata(client);
++ struct m62332_data *data = iio_priv(indio_dev);
++ int ret;
++
++ ret = m62332_set_value(indio_dev, data->save[0], 0);
++ if (ret < 0)
++ return ret;
++
++ return m62332_set_value(indio_dev, data->save[1], 1);
++}
++
++static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
++#define M62332_PM_OPS (&m62332_pm_ops)
++#else
++#define M62332_PM_OPS NULL
++#endif
++
++static const struct iio_info m62332_info = {
++ .read_raw = m62332_read_raw,
++ .write_raw = m62332_write_raw,
++ .driver_module = THIS_MODULE,
++};
++
++#define M62332_CHANNEL(chan) { \
++ .type = IIO_VOLTAGE, \
++ .indexed = 1, \
++ .output = 1, \
++ .channel = (chan), \
++ .datasheet_name = "CH" #chan, \
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
++ BIT(IIO_CHAN_INFO_SCALE), \
++}
++
++static const struct iio_chan_spec m62332_channels[] = {
++ M62332_CHANNEL(0),
++ M62332_CHANNEL(1)
++};
++
++static int m62332_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct m62332_data *data;
++ struct iio_dev *indio_dev;
++
++ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
++ if (!indio_dev)
++ return -ENOMEM;
++ data = iio_priv(indio_dev);
++ i2c_set_clientdata(client, indio_dev);
++ data->client = client;
++
++ data->vcc = devm_regulator_get(&client->dev, "VCC");
++ if (IS_ERR(data->vcc))
++ return PTR_ERR(data->vcc);
++
++ /* establish that the iio_dev is a child of the i2c device */
++ indio_dev->dev.parent = &client->dev;
++
++ indio_dev->num_channels = 2;
++ indio_dev->channels = m62332_channels;
++ indio_dev->modes = INDIO_DIRECT_MODE;
++ indio_dev->info = &m62332_info;
++
++ data->vref_mv = regulator_get_voltage(data->vcc) / 1000; /* mV */
++
++ iio_map_array_register(indio_dev, client->dev.platform_data);
++
++ return iio_device_register(indio_dev);
++}
++
++static int m62332_remove(struct i2c_client *client)
++{
++ struct iio_dev *indio_dev = i2c_get_clientdata(client);
++
++ iio_device_unregister(indio_dev);
++ iio_map_array_unregister(indio_dev);
++ return 0;
++}
++
++static const struct i2c_device_id m62332_id[] = {
++ { "m62332", },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, m62332_id);
++
++static struct i2c_driver m62332_driver = {
++ .driver = {
++ .name = "m62332",
++ .pm = M62332_PM_OPS,
++ },
++ .probe = m62332_probe,
++ .remove = m62332_remove,
++ .id_table = m62332_id,
++};
++module_i2c_driver(m62332_driver);
++
++MODULE_AUTHOR("Dmitry Eremin-Solenikov");
++MODULE_DESCRIPTION("M62332 8-bit DAC");
++MODULE_LICENSE("GPL v2");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0002-mfd-add-new-driver-for-Sharp-LoCoMo.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0002-mfd-add-new-driver-for-Sharp-LoCoMo.patch
new file mode 100644
index 0000000..4888b69
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0002-mfd-add-new-driver-for-Sharp-LoCoMo.patch
@@ -0,0 +1,603 @@
+From 39201b5a779092078e865dd33db6d24e1e0a4c6f Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 02:56:08 +0400
+Subject: [PATCH 02/20] mfd: add new driver for Sharp LoCoMo
+
+LoCoMo is a GA used on Sharp Zaurus SL-5x00. Current driver does has
+several design issues (special bus instead of platform bus, doesn't use
+mfd-core, etc).
+
+Implement 'core' parts of locomo support as an mfd driver.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/mfd/Kconfig | 10 ++
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/locomo.c | 361 +++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/locomo.h | 173 ++++++++++++++++++++++
+ 4 files changed, 545 insertions(+)
+ create mode 100644 drivers/mfd/locomo.c
+ create mode 100644 include/linux/mfd/locomo.h
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 38356e3..95fd73e 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -1378,6 +1378,16 @@ config MFD_STW481X
+ in various ST Microelectronics and ST-Ericsson embedded
+ Nomadik series.
+
++config MFD_LOCOMO
++ bool "Sharp LoCoMo support"
++ depends on ARM
++ select MFD_CORE
++ select IRQ_DOMAIN
++ select REGMAP_MMIO
++ help
++ Support for Sharp LoCoMo Grid Array found in Sharp SL-5x00
++ PDA family.
++
+ menu "Multimedia Capabilities Port drivers"
+ depends on ARCH_SA1100
+
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index 19f3d74..8deda90 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -178,6 +178,7 @@ obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o
+ obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
+ obj-$(CONFIG_MFD_DLN2) += dln2.o
+ obj-$(CONFIG_MFD_RT5033) += rt5033.o
++obj-$(CONFIG_MFD_LOCOMO) += locomo.o
+
+ intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
+ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+diff --git a/drivers/mfd/locomo.c b/drivers/mfd/locomo.c
+new file mode 100644
+index 0000000..bc7cc94
+--- /dev/null
++++ b/drivers/mfd/locomo.c
+@@ -0,0 +1,361 @@
++/*
++ * Sharp LoCoMo support
++ *
++ * Based on old driver at arch/arm/common/locomo.c
++ *
++ * 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.
++ */
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/irqdomain.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/mfd/core.h>
++#include <linux/mfd/locomo.h>
++
++/* LoCoMo Interrupts */
++#define IRQ_LOCOMO_KEY (0)
++#define IRQ_LOCOMO_GPIO (1)
++#define IRQ_LOCOMO_LT (2)
++#define IRQ_LOCOMO_SPI (3)
++
++#define LOCOMO_NR_IRQS (4)
++
++/* the following is the overall data for the locomo chip */
++struct locomo {
++ struct device *dev;
++ unsigned int irq;
++ spinlock_t lock;
++ struct irq_domain *domain;
++ struct regmap *regmap;
++};
++
++static struct resource locomo_kbd_resources[] = {
++ DEFINE_RES_IRQ(IRQ_LOCOMO_KEY),
++};
++
++static struct resource locomo_gpio_resources[] = {
++ DEFINE_RES_IRQ(IRQ_LOCOMO_GPIO),
++};
++
++static struct locomo_gpio_platform_data locomo_gpio_pdata = {
++};
++
++static struct resource locomo_lt_resources[] = {
++ DEFINE_RES_IRQ(IRQ_LOCOMO_LT),
++};
++
++static struct resource locomo_spi_resources[] = {
++ DEFINE_RES_IRQ(IRQ_LOCOMO_SPI),
++};
++
++static struct locomo_lcd_platform_data locomo_lcd_pdata = {
++};
++
++static struct mfd_cell locomo_cells[] = {
++ {
++ .name = "locomo-kbd",
++ .resources = locomo_kbd_resources,
++ .num_resources = ARRAY_SIZE(locomo_kbd_resources),
++ },
++ {
++ .name = "locomo-gpio",
++ .resources = locomo_gpio_resources,
++ .num_resources = ARRAY_SIZE(locomo_gpio_resources),
++ .platform_data = &locomo_gpio_pdata,
++ .pdata_size = sizeof(locomo_gpio_pdata),
++ },
++ {
++ .name = "locomo-lt", /* Long time timer */
++ .resources = locomo_lt_resources,
++ .num_resources = ARRAY_SIZE(locomo_lt_resources),
++ },
++ {
++ .name = "locomo-spi",
++ .resources = locomo_spi_resources,
++ .num_resources = ARRAY_SIZE(locomo_spi_resources),
++ },
++ {
++ .name = "locomo-led",
++ },
++ {
++ .name = "locomo-backlight",
++ },
++ {
++ .name = "locomo-lcd",
++ .platform_data = &locomo_lcd_pdata,
++ .pdata_size = sizeof(locomo_lcd_pdata),
++ },
++ {
++ .name = "locomo-i2c",
++ },
++};
++
++/* IRQ support */
++static void locomo_handler(unsigned int irq, struct irq_desc *desc)
++{
++ struct locomo *lchip = irq_get_handler_data(irq);
++ struct irq_chip *irqchip = irq_desc_get_chip(desc);
++ unsigned int req;
++
++ chained_irq_enter(irqchip, desc);
++
++ /* check why this interrupt was generated */
++ while (1) {
++ regmap_read(lchip->regmap, LOCOMO_ICR, &req);
++ req &= 0x0f00;
++
++ if (!req)
++ break;
++
++ irq = ffs(req) - 9;
++ generic_handle_irq(irq_find_mapping(lchip->domain, irq));
++ }
++
++ chained_irq_exit(irqchip, desc);
++}
++
++static void locomo_ack_irq(struct irq_data *d)
++{
++}
++
++static void locomo_mask_irq(struct irq_data *d)
++{
++ struct locomo *lchip = irq_data_get_irq_chip_data(d);
++
++ regmap_update_bits(lchip->regmap, LOCOMO_ICR,
++ 0x0010 << d->hwirq,
++ 0);
++}
++
++static void locomo_unmask_irq(struct irq_data *d)
++{
++ struct locomo *lchip = irq_data_get_irq_chip_data(d);
++
++ regmap_update_bits(lchip->regmap, LOCOMO_ICR,
++ (0x0010 << d->hwirq),
++ (0x0010 << d->hwirq));
++}
++
++static struct irq_chip locomo_chip = {
++ .name = "LOCOMO",
++ .irq_ack = locomo_ack_irq,
++ .irq_mask = locomo_mask_irq,
++ .irq_unmask = locomo_unmask_irq,
++};
++
++static int locomo_irq_map(struct irq_domain *d, unsigned int virq,
++ irq_hw_number_t hwirq)
++{
++ struct locomo *locomo = d->host_data;
++
++ irq_set_chip_data(virq, locomo);
++ irq_set_chip_and_handler(virq, &locomo_chip,
++ handle_level_irq);
++ set_irq_flags(virq, IRQF_VALID);
++
++ return 0;
++}
++
++static void locomo_irq_unmap(struct irq_domain *d, unsigned int virq)
++{
++ set_irq_flags(virq, 0);
++ irq_set_chip_and_handler(virq, NULL, NULL);
++ irq_set_chip_data(virq, NULL);
++}
++
++static struct irq_domain_ops locomo_irq_ops = {
++ .map = locomo_irq_map,
++ .unmap = locomo_irq_unmap,
++ .xlate = irq_domain_xlate_onecell,
++};
++
++static int locomo_setup_irq(struct locomo *lchip)
++{
++ lchip->domain = irq_domain_add_simple(NULL, LOCOMO_NR_IRQS, 0,
++ &locomo_irq_ops, lchip);
++ if (!lchip->domain) {
++ dev_err(lchip->dev, "Failed to register irqdomain\n");
++ return -ENOSYS;
++ }
++
++ /*
++ * Install handler for IRQ_LOCOMO_HW.
++ */
++ irq_set_irq_type(lchip->irq, IRQ_TYPE_EDGE_FALLING);
++ irq_set_handler_data(lchip->irq, lchip);
++ irq_set_chained_handler(lchip->irq, locomo_handler);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int locomo_suspend(struct device *dev)
++{
++ struct locomo *lchip = dev_get_drvdata(dev);
++
++ /* AUDIO */
++ regmap_write(lchip->regmap, LOCOMO_PAIF, 0x00);
++ regmap_write(lchip->regmap, LOCOMO_DAC, 0x00);
++
++ /*
++ * Original code disabled the clock depending on leds settings
++ * However we disable leds before suspend, thus it's safe
++ * to just assume this setting.
++ */
++ /* CLK32 off */
++ regmap_write(lchip->regmap, LOCOMO_C32K, 0x00);
++
++ /* 22MHz/24MHz clock off */
++ regmap_write(lchip->regmap, LOCOMO_ACC, 0x00);
++
++ return 0;
++}
++
++static int locomo_resume(struct device *dev)
++{
++ struct locomo *lchip = dev_get_drvdata(dev);
++ regmap_write(lchip->regmap, LOCOMO_C32K, 0x00);
++
++ return 0;
++}
++static SIMPLE_DEV_PM_OPS(locomo_pm, locomo_suspend, locomo_resume);
++#define LOCOMO_PM (&locomo_pm)
++#else
++#define LOCOMO_PM NULL
++#endif
++
++static const struct regmap_config locomo_regmap_config = {
++ .name = "LoCoMo",
++ .reg_bits = 8,
++ .reg_stride = 4,
++ .val_bits = 16,
++ .cache_type = REGCACHE_NONE,
++ .max_register = 0xec,
++};
++
++static int locomo_probe(struct platform_device *dev)
++{
++ struct locomo_platform_data *pdata = dev_get_platdata(&dev->dev);
++ struct resource *mem;
++ void __iomem *base;
++ struct locomo *lchip;
++ unsigned int r;
++ int ret = -ENODEV;
++
++ lchip = devm_kzalloc(&dev->dev, sizeof(struct locomo), GFP_KERNEL);
++ if (!lchip)
++ return -ENOMEM;
++
++ spin_lock_init(&lchip->lock);
++ lchip->dev = &dev->dev;
++
++ lchip->irq = platform_get_irq(dev, 0);
++ if (lchip->irq < 0)
++ return -ENXIO;
++
++ mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&dev->dev, mem);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ lchip->regmap = devm_regmap_init_mmio(&dev->dev, base,
++ &locomo_regmap_config);
++ if (IS_ERR(lchip->regmap))
++ return PTR_ERR(lchip->regmap);
++
++ if (pdata) {
++ locomo_gpio_pdata.gpio_base = pdata->gpio_base;
++ locomo_lcd_pdata.comadj = pdata->comadj;
++ } else {
++ locomo_gpio_pdata.gpio_base = -1;
++ locomo_lcd_pdata.comadj = 128;
++ }
++
++ platform_set_drvdata(dev, lchip);
++
++ regmap_read(lchip->regmap, LOCOMO_VER, &r);
++ dev_info(&dev->dev, "LoCoMo Chip: %04x\n", r);
++
++ /* locomo initialize */
++ regmap_write(lchip->regmap, LOCOMO_ICR, 0);
++
++ /* Longtime timer */
++ regmap_write(lchip->regmap, LOCOMO_LTINT, 0);
++ /* SPI */
++ regmap_write(lchip->regmap, LOCOMO_SPIIE, 0);
++
++ /* init DAC */
++ regmap_update_bits(lchip->regmap, LOCOMO_DAC,
++ LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB,
++ LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB);
++
++ /*
++ * The interrupt controller must be initialised before any
++ * other device to ensure that the interrupts are available.
++ */
++ ret = locomo_setup_irq(lchip);
++ if (ret < 0)
++ goto err_add;
++
++ ret = mfd_add_devices(&dev->dev, dev->id,
++ locomo_cells, ARRAY_SIZE(locomo_cells),
++ mem, -1, lchip->domain);
++ if (ret)
++ goto err_add;
++
++ return 0;
++
++err_add:
++ irq_set_chained_handler(lchip->irq, NULL);
++ irq_set_handler_data(lchip->irq, NULL);
++ if (lchip->domain)
++ irq_domain_remove(lchip->domain);
++
++ return ret;
++}
++
++static int locomo_remove(struct platform_device *dev)
++{
++ struct locomo *lchip = platform_get_drvdata(dev);
++
++ if (!lchip)
++ return 0;
++
++ mfd_remove_devices(&dev->dev);
++
++ irq_set_chained_handler(lchip->irq, NULL);
++ irq_set_handler_data(lchip->irq, NULL);
++ if (lchip->domain)
++ irq_domain_remove(lchip->domain);
++
++ return 0;
++}
++
++static struct platform_driver locomo_device_driver = {
++ .probe = locomo_probe,
++ .remove = locomo_remove,
++ .driver = {
++ .name = "locomo",
++ .pm = LOCOMO_PM,
++ },
++};
++
++module_platform_driver(locomo_device_driver);
++
++MODULE_DESCRIPTION("Sharp LoCoMo core driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
++MODULE_ALIAS("platform:locomo");
+diff --git a/include/linux/mfd/locomo.h b/include/linux/mfd/locomo.h
+new file mode 100644
+index 0000000..6729767
+--- /dev/null
++++ b/include/linux/mfd/locomo.h
+@@ -0,0 +1,173 @@
++/*
++ * include/linux/mfd/locomo.h
++ *
++ * This file contains the definitions for the LoCoMo G/A Chip
++ *
++ * (C) Copyright 2004 John Lenz
++ *
++ * May be copied or modified under the terms of the GNU General Public
++ * License. See linux/COPYING for more information.
++ *
++ * Based on sa1111.h
++ */
++#ifndef _ASM_ARCH_LOCOMO
++#define _ASM_ARCH_LOCOMO
++
++/* LOCOMO version */
++#define LOCOMO_VER 0x00
++
++/* Pin status */
++#define LOCOMO_ST 0x04
++
++/* Pin status */
++#define LOCOMO_C32K 0x08
++
++/* Interrupt controller */
++#define LOCOMO_ICR 0x0C
++
++/* MCS decoder for boot selecting */
++#define LOCOMO_MCSX0 0x10
++#define LOCOMO_MCSX1 0x14
++#define LOCOMO_MCSX2 0x18
++#define LOCOMO_MCSX3 0x1c
++
++/* Touch panel controller */
++#define LOCOMO_ASD 0x20 /* AD start delay */
++#define LOCOMO_HSD 0x28 /* HSYS delay */
++#define LOCOMO_HSC 0x2c /* HSYS period */
++#define LOCOMO_TADC 0x30 /* tablet ADC clock */
++
++/* Backlight controller: TFT signal */
++#define LOCOMO_TC 0x38 /* TFT control signal */
++#define LOCOMO_CPSD 0x3c /* CPS delay */
++
++/* Keyboard controller */
++#define LOCOMO_KIB 0x40 /* KIB level */
++#define LOCOMO_KSC 0x44 /* KSTRB control */
++#define LOCOMO_KCMD 0x48 /* KSTRB command */
++#define LOCOMO_KIC 0x4c /* Key interrupt */
++
++/* Audio clock */
++#define LOCOMO_ACC 0x54 /* Audio clock */
++#define LOCOMO_ACC_XON 0x80
++#define LOCOMO_ACC_XEN 0x40
++#define LOCOMO_ACC_XSEL0 0x00
++#define LOCOMO_ACC_XSEL1 0x20
++#define LOCOMO_ACC_MCLKEN 0x10
++#define LOCOMO_ACC_64FSEN 0x08
++#define LOCOMO_ACC_CLKSEL000 0x00 /* mclk 2 */
++#define LOCOMO_ACC_CLKSEL001 0x01 /* mclk 3 */
++#define LOCOMO_ACC_CLKSEL010 0x02 /* mclk 4 */
++#define LOCOMO_ACC_CLKSEL011 0x03 /* mclk 6 */
++#define LOCOMO_ACC_CLKSEL100 0x04 /* mclk 8 */
++#define LOCOMO_ACC_CLKSEL101 0x05 /* mclk 12 */
++
++/* SPI interface */
++#define LOCOMO_SPIMD 0x60 /* SPI mode setting */
++#define LOCOMO_SPIMD_LOOPBACK (1 << 15) /* loopback tx to rx */
++#define LOCOMO_SPIMD_MSB1ST (1 << 14) /* send MSB first */
++#define LOCOMO_SPIMD_DOSTAT (1 << 13) /* transmit line is idle high */
++#define LOCOMO_SPIMD_TCPOL (1 << 11) /* transmit CPOL (maybe affects CPHA) */
++#define LOCOMO_SPIMD_RCPOL (1 << 10) /* receive CPOL (maybe affects CPHA) */
++#define LOCOMO_SPIMD_TDINV (1 << 9) /* invert transmit line */
++#define LOCOMO_SPIMD_RDINV (1 << 8) /* invert receive line */
++#define LOCOMO_SPIMD_XON (1 << 7) /* enable spi controller clock */
++#define LOCOMO_SPIMD_XEN (1 << 6) /* clock bit write enable */
++#define LOCOMO_SPIMD_XSEL 0x0018 /* clock select */
++/* xon must be off when enabling xen, wait 300 us before xon -> 1 */
++#define CLOCK_18MHZ 0 /* 18,432 MHz clock */
++#define CLOCK_22MHZ 1 /* 22,5792 MHz clock */
++#define CLOCK_25MHZ 2 /* 24,576 MHz clock */
++#define LOCOMO_SPIMD_CLKSEL 0x7
++#define DIV_1 0 /* don't divide clock */
++#define DIV_2 1 /* divide clock by two */
++#define DIV_4 2 /* divide clock by four */
++#define DIV_8 3 /* divide clock by eight*/
++#define DIV_64 4 /* divide clock by 64 */
++
++#define LOCOMO_SPICT 0x64 /* SPI mode control */
++#define LOCOMO_SPICT_CRC16_7_B (1 << 15) /* 0: crc16 1: crc7 */
++#define LOCOMO_SPICT_CRCRX_TX_B (1 << 14)
++#define LOCOMO_SPICT_CRCRESET_B (1 << 13)
++#define LOCOMO_SPICT_CEN (1 << 7) /* ?? enable */
++#define LOCOMO_SPICT_CS (1 << 6) /* chip select */
++#define LOCOMO_SPICT_UNIT16 (1 << 5) /* 0: 8 bit, 1: 16 bit*/
++#define LOCOMO_SPICT_ALIGNEN (1 << 2) /* align transfer enable */
++#define LOCOMO_SPICT_RXWEN (1 << 1) /* continuous receive */
++#define LOCOMO_SPICT_RXUEN (1 << 0) /* aligned receive */
++
++#define LOCOMO_SPIST 0x68 /* SPI status */
++#define LOCOMO_SPI_TEND (1 << 3) /* Transfer end bit */
++#define LOCOMO_SPI_REND (1 << 2) /* Receive end bit */
++#define LOCOMO_SPI_RFW (1 << 1) /* write buffer bit */
++#define LOCOMO_SPI_RFR (1) /* read buffer bit */
++
++#define LOCOMO_SPIIS 0x70 /* SPI interrupt status */
++#define LOCOMO_SPIWE 0x74 /* SPI interrupt status write enable */
++#define LOCOMO_SPIIE 0x78 /* SPI interrupt enable */
++#define LOCOMO_SPIIR 0x7c /* SPI interrupt request */
++#define LOCOMO_SPITD 0x80 /* SPI transfer data write */
++#define LOCOMO_SPIRD 0x84 /* SPI receive data read */
++#define LOCOMO_SPITS 0x88 /* SPI transfer data shift */
++#define LOCOMO_SPIRS 0x8C /* SPI receive data shift */
++
++/* GPIO */
++#define LOCOMO_GPD 0x90 /* GPIO direction */
++#define LOCOMO_GPE 0x94 /* GPIO input enable */
++#define LOCOMO_GPL 0x98 /* GPIO level */
++#define LOCOMO_GPO 0x9c /* GPIO out data setting */
++#define LOCOMO_GRIE 0xa0 /* GPIO rise detection */
++#define LOCOMO_GFIE 0xa4 /* GPIO fall detection */
++#define LOCOMO_GIS 0xa8 /* GPIO edge detection status */
++#define LOCOMO_GWE 0xac /* GPIO status write enable */
++#define LOCOMO_GIE 0xb0 /* GPIO interrupt enable */
++#define LOCOMO_GIR 0xb4 /* GPIO interrupt request */
++
++/* Front light adjustment controller */
++#define LOCOMO_ALS 0xc8 /* Adjust light cycle */
++#define LOCOMO_ALS_EN 0x8000
++#define LOCOMO_ALD 0xcc /* Adjust light duty */
++
++/* PCM audio interface */
++#define LOCOMO_PAIF 0xd0 /* PCM audio interface */
++#define LOCOMO_PAIF_SCINV 0x20
++#define LOCOMO_PAIF_SCEN 0x10
++#define LOCOMO_PAIF_LRCRST 0x08
++#define LOCOMO_PAIF_LRCEVE 0x04
++#define LOCOMO_PAIF_LRCINV 0x02
++#define LOCOMO_PAIF_LRCEN 0x01
++
++/* Long time timer */
++#define LOCOMO_LTC 0xd8 /* LTC interrupt setting */
++#define LOCOMO_LTINT 0xdc /* LTC interrupt */
++
++/* DAC control signal for LCD (COMADJ ) */
++#define LOCOMO_DAC 0xe0
++/* DAC control */
++#define LOCOMO_DAC_SCLOEB 0x08 /* SCL pin output data */
++#define LOCOMO_DAC_TEST 0x04 /* Test bit */
++#define LOCOMO_DAC_SDA 0x02 /* SDA pin level (read-only) */
++#define LOCOMO_DAC_SDAOEB 0x01 /* SDA pin output data */
++
++/* LED controller */
++#define LOCOMO_LPT0 0xe8
++#define LOCOMO_LPT1 0xec
++#define LOCOMO_LPT_TOFH 0x80
++#define LOCOMO_LPT_TOFL 0x08
++#define LOCOMO_LPT_TOH(TOH) ((TOH & 0x7) << 4)
++#define LOCOMO_LPT_TOL(TOL) ((TOL & 0x7))
++
++struct locomo_gpio_platform_data {
++ unsigned int gpio_base;
++};
++
++struct locomo_lcd_platform_data {
++ u8 comadj;
++};
++
++struct locomo_platform_data {
++ unsigned int gpio_base;
++ u8 comadj;
++};
++
++#endif
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0003-leds-port-locomo-leds-driver-to-new-locomo-core.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0003-leds-port-locomo-leds-driver-to-new-locomo-core.patch
new file mode 100644
index 0000000..127129c
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0003-leds-port-locomo-leds-driver-to-new-locomo-core.patch
@@ -0,0 +1,187 @@
+From 9ce7fe614b1251e1eb04b714bcb07d48d664e9ca Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 03:04:12 +0400
+Subject: [PATCH 03/20] leds: port locomo leds driver to new locomo core
+
+Adapt locomo leds driver to new locomo core setup.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/leds/Kconfig | 1 -
+ drivers/leds/leds-locomo.c | 116 +++++++++++++++++++++++++--------------------
+ 2 files changed, 65 insertions(+), 52 deletions(-)
+
+diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
+index 25b320d..aec42ae 100644
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -79,7 +79,6 @@ config LEDS_LM3642
+ config LEDS_LOCOMO
+ tristate "LED Support for Locomo device"
+ depends on LEDS_CLASS
+- depends on SHARP_LOCOMO
+ help
+ This option enables support for the LEDs on Sharp Locomo.
+ Zaurus models SL-5500 and SL-5600.
+diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
+index 80ba048..cecc585 100644
+--- a/drivers/leds/leds-locomo.c
++++ b/drivers/leds/leds-locomo.c
+@@ -9,89 +9,103 @@
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/device.h>
+ #include <linux/leds.h>
+-
+-#include <mach/hardware.h>
+-#include <asm/hardware/locomo.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/mfd/locomo.h>
++
++struct locomo_led {
++ struct led_classdev led;
++ struct regmap *regmap;
++ unsigned int reg;
++};
+
+ static void locomoled_brightness_set(struct led_classdev *led_cdev,
+- enum led_brightness value, int offset)
++ enum led_brightness value)
+ {
+- struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->dev->parent);
+- unsigned long flags;
+-
+- local_irq_save(flags);
+- if (value)
+- locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + offset);
+- else
+- locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + offset);
+- local_irq_restore(flags);
++ struct locomo_led *led = container_of(led_cdev, struct locomo_led, led);
++
++ regmap_write(led->regmap, led->reg,
++ value ? LOCOMO_LPT_TOFH : LOCOMO_LPT_TOFL);
+ }
+
+-static void locomoled_brightness_set0(struct led_classdev *led_cdev,
+- enum led_brightness value)
++static int locomo_led_register(
++ struct locomo_led *led,
++ struct device *dev,
++ const char *name,
++ const char *trigger,
++ struct regmap *regmap,
++ unsigned int reg)
+ {
+- locomoled_brightness_set(led_cdev, value, LOCOMO_LPT0);
++ led->led.name = name;
++ led->led.flags = LED_CORE_SUSPENDRESUME;
++ led->led.default_trigger = trigger;
++ led->led.brightness_set = locomoled_brightness_set;
++ led->regmap = regmap;
++ led->reg = reg;
++
++ return led_classdev_register(dev, &led->led);
+ }
+
+-static void locomoled_brightness_set1(struct led_classdev *led_cdev,
+- enum led_brightness value)
++static int locomoled_probe(struct platform_device *pdev)
+ {
+- locomoled_brightness_set(led_cdev, value, LOCOMO_LPT1);
+-}
++ int ret;
++ struct locomo_led *leds;
++ struct regmap *regmap;
+
+-static struct led_classdev locomo_led0 = {
+- .name = "locomo:amber:charge",
+- .default_trigger = "main-battery-charging",
+- .brightness_set = locomoled_brightness_set0,
+-};
++ leds = devm_kzalloc(&pdev->dev, 2 * sizeof(*leds), GFP_KERNEL);
++ if (!leds)
++ return -ENOMEM;
+
+-static struct led_classdev locomo_led1 = {
+- .name = "locomo:green:mail",
+- .default_trigger = "nand-disk",
+- .brightness_set = locomoled_brightness_set1,
+-};
++ regmap = dev_get_regmap(pdev->dev.parent, NULL);
++ if (!regmap)
++ return -ENODEV;
+
+-static int locomoled_probe(struct locomo_dev *ldev)
+-{
+- int ret;
++ platform_set_drvdata(pdev, leds);
+
+- ret = led_classdev_register(&ldev->dev, &locomo_led0);
++ ret = locomo_led_register(leds,
++ &pdev->dev,
++ "locomo:amber:charge",
++ "main-battery-charging",
++ regmap,
++ LOCOMO_LPT0);
+ if (ret < 0)
+ return ret;
+
+- ret = led_classdev_register(&ldev->dev, &locomo_led1);
++ ret = locomo_led_register(leds + 1,
++ &pdev->dev,
++ "locomo:green:mail",
++ "mmc0",
++ regmap,
++ LOCOMO_LPT1);
+ if (ret < 0)
+- led_classdev_unregister(&locomo_led0);
++ led_classdev_unregister(&leds[0].led);
+
+ return ret;
+ }
+
+-static int locomoled_remove(struct locomo_dev *dev)
++static int locomoled_remove(struct platform_device *pdev)
+ {
+- led_classdev_unregister(&locomo_led0);
+- led_classdev_unregister(&locomo_led1);
++ struct locomo_led *leds = platform_get_drvdata(pdev);
++
++ led_classdev_unregister(&leds[0].led);
++ led_classdev_unregister(&leds[1].led);
++
+ return 0;
+ }
+
+-static struct locomo_driver locomoled_driver = {
+- .drv = {
+- .name = "locomoled"
++static struct platform_driver locomoled_driver = {
++ .driver = {
++ .name = "locomo-led"
+ },
+- .devid = LOCOMO_DEVID_LED,
+ .probe = locomoled_probe,
+ .remove = locomoled_remove,
+ };
+
+-static int __init locomoled_init(void)
+-{
+- return locomo_driver_register(&locomoled_driver);
+-}
+-module_init(locomoled_init);
++module_platform_driver(locomoled_driver);
+
+ MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+ MODULE_DESCRIPTION("Locomo LED driver");
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:locomo-led");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0004-input-convert-LoCoMo-keyboard-driver-to-use-new-loco.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0004-input-convert-LoCoMo-keyboard-driver-to-use-new-loco.patch
new file mode 100644
index 0000000..03a2023
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0004-input-convert-LoCoMo-keyboard-driver-to-use-new-loco.patch
@@ -0,0 +1,480 @@
+From c546d0c1ae4e7134cfc2668bf4e01753dfb9a98d Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 03:06:46 +0400
+Subject: [PATCH 04/20] input: convert LoCoMo keyboard driver to use new locomo
+ core
+
+As LoCoMo is switching to new device model, adapt keyboard driver to
+support new locomo core driver.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/input/keyboard/Kconfig | 1 -
+ drivers/input/keyboard/locomokbd.c | 271 +++++++++++++++++++------------------
+ 2 files changed, 143 insertions(+), 129 deletions(-)
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index a89ba7c..4e20538 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -337,7 +337,6 @@ config KEYBOARD_LM8333
+
+ config KEYBOARD_LOCOMO
+ tristate "LoCoMo Keyboard Support"
+- depends on SHARP_LOCOMO
+ help
+ Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
+
+diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
+index c94d610..eed0a94 100644
+--- a/drivers/input/keyboard/locomokbd.c
++++ b/drivers/input/keyboard/locomokbd.c
+@@ -23,37 +23,37 @@
+ *
+ */
+
+-#include <linux/slab.h>
+-#include <linux/module.h>
++#include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/input.h>
+-#include <linux/delay.h>
+-#include <linux/device.h>
+ #include <linux/interrupt.h>
+-#include <linux/ioport.h>
+-
+-#include <asm/hardware/locomo.h>
+-#include <asm/irq.h>
+-
+-MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+-MODULE_DESCRIPTION("LoCoMo keyboard driver");
+-MODULE_LICENSE("GPL");
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/mfd/locomo.h>
+
+-#define LOCOMOKBD_NUMKEYS 128
++/* There is one minor difference between mappings on poodle and collie */
++#include <asm/mach-types.h>
+
+ #define KEY_ACTIVITY KEY_F16
+ #define KEY_CONTACT KEY_F18
+ #define KEY_CENTER KEY_F15
+
++#define KB_ROWS 16
++#define KB_COLS 8
++#define LOCOMOKBD_NUMKEYS (KB_ROWS * KB_COLS)
++#define SCANCODE(c, r) (((c)<<4) + (r) + 1)
++
+ static const unsigned char
+ locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+ 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
+- 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */
++ 0, 0, 0, 0, 0, 0, 0, KEY_MENU, 0, KEY_CONTACT, /* 10 - 19 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
+ 0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
+ KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */
+- KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */
++ KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_KPENTER, 0, 0, /* 60 - 69 */
+ KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */
+ 0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */
+ KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */
+@@ -62,20 +62,14 @@ locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+ KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */
+ };
+
+-#define KB_ROWS 16
+-#define KB_COLS 8
+-#define KB_ROWMASK(r) (1 << (r))
+-#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 )
+-
+ #define KB_DELAY 8
+-#define SCAN_INTERVAL (HZ/10)
+
+ struct locomokbd {
+ unsigned char keycode[LOCOMOKBD_NUMKEYS];
+ struct input_dev *input;
+- char phys[32];
+
+- unsigned long base;
++ struct regmap *regmap;
++ int irq;
+ spinlock_t lock;
+
+ struct timer_list timer;
+@@ -84,37 +78,33 @@ struct locomokbd {
+ };
+
+ /* helper functions for reading the keyboard matrix */
+-static inline void locomokbd_charge_all(unsigned long membase)
++static inline void locomokbd_charge_all(struct locomokbd *locomokbd)
+ {
+- locomo_writel(0x00FF, membase + LOCOMO_KSC);
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, 0x00ff);
+ }
+
+-static inline void locomokbd_activate_all(unsigned long membase)
++static inline void locomokbd_activate_all(struct locomokbd *locomokbd)
+ {
+- unsigned long r;
+-
+- locomo_writel(0, membase + LOCOMO_KSC);
+- r = locomo_readl(membase + LOCOMO_KIC);
+- r &= 0xFEFF;
+- locomo_writel(r, membase + LOCOMO_KIC);
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, 0);
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x100, 0);
+ }
+
+-static inline void locomokbd_activate_col(unsigned long membase, int col)
++static inline void locomokbd_activate_col(struct locomokbd *locomokbd, int col)
+ {
+ unsigned short nset;
+ unsigned short nbset;
+
+- nset = 0xFF & ~(1 << col);
++ nset = 0xFF & ~BIT(col);
+ nbset = (nset << 8) + nset;
+- locomo_writel(nbset, membase + LOCOMO_KSC);
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, nbset);
+ }
+
+-static inline void locomokbd_reset_col(unsigned long membase, int col)
++static inline void locomokbd_reset_col(struct locomokbd *locomokbd, int col)
+ {
+ unsigned short nbset;
+
+- nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
+- locomo_writel(nbset, membase + LOCOMO_KSC);
++ nbset = ((0xFF & ~BIT(col)) << 8) + 0xFF;
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, nbset);
+ }
+
+ /*
+@@ -129,24 +119,25 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
+ unsigned int row, col, rowd;
+ unsigned long flags;
+ unsigned int num_pressed;
+- unsigned long membase = locomokbd->base;
++ bool esc_pressed = false;
+
+ spin_lock_irqsave(&locomokbd->lock, flags);
+
+- locomokbd_charge_all(membase);
++ locomokbd_charge_all(locomokbd);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+-
+- locomokbd_activate_col(membase, col);
++ udelay(KB_DELAY);
++ locomokbd_activate_col(locomokbd, col);
+ udelay(KB_DELAY);
+
+- rowd = ~locomo_readl(membase + LOCOMO_KIB);
++ regmap_read(locomokbd->regmap, LOCOMO_KIB, &rowd);
++ rowd = ~rowd;
+ for (row = 0; row < KB_ROWS; row++) {
+ unsigned int scancode, pressed, key;
+
+ scancode = SCANCODE(col, row);
+- pressed = rowd & KB_ROWMASK(row);
++ pressed = rowd & BIT(row);
+ key = locomokbd->keycode[scancode];
+
+ input_report_key(locomokbd->input, key, pressed);
+@@ -158,29 +149,30 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
+ /* The "Cancel/ESC" key is labeled "On/Off" on
+ * Collie and Poodle and should suspend the device
+ * if it was pressed for more than a second. */
+- if (unlikely(key == KEY_ESC)) {
+- if (!time_after(jiffies,
+- locomokbd->suspend_jiffies + HZ))
+- continue;
+- if (locomokbd->count_cancel++
+- != (HZ/SCAN_INTERVAL + 1))
+- continue;
+- input_event(locomokbd->input, EV_PWR,
+- KEY_SUSPEND, 1);
+- locomokbd->suspend_jiffies = jiffies;
+- } else
+- locomokbd->count_cancel = 0;
++ if (unlikely(key == KEY_ESC))
++ esc_pressed = true;
+ }
+- locomokbd_reset_col(membase, col);
++ locomokbd_reset_col(locomokbd, col);
+ }
+- locomokbd_activate_all(membase);
++ locomokbd_activate_all(locomokbd);
+
+ input_sync(locomokbd->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+- mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
++ mod_timer(&locomokbd->timer, jiffies + msecs_to_jiffies(100));
+ else
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x10, 0x10);
++
++
++ if (esc_pressed && time_after(jiffies,
++ locomokbd->suspend_jiffies + msecs_to_jiffies(1000))) {
++ if (locomokbd->count_cancel++ > (20)) {
++ input_event(locomokbd->input, EV_PWR,
++ KEY_SUSPEND, 1);
++ locomokbd->suspend_jiffies = jiffies;
++ }
++ } else
+ locomokbd->count_cancel = 0;
+
+ spin_unlock_irqrestore(&locomokbd->lock, flags);
+@@ -192,18 +184,18 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
+ static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
+ {
+ struct locomokbd *locomokbd = dev_id;
+- u16 r;
++ unsigned int r;
+
+- r = locomo_readl(locomokbd->base + LOCOMO_KIC);
++
++ regmap_read(locomokbd->regmap, LOCOMO_KIC, &r);
+ if ((r & 0x0001) == 0)
+ return IRQ_HANDLED;
+
+- locomo_writel(r & ~0x0100, locomokbd->base + LOCOMO_KIC); /* Ack */
++ /* Mask and Ack */
++ regmap_write(locomokbd->regmap, LOCOMO_KIC, r & ~0x110);
+
+- /** wait chattering delay **/
+- udelay(100);
++ mod_timer(&locomokbd->timer, jiffies + msecs_to_jiffies(1));
+
+- locomokbd_scankeyboard(locomokbd);
+ return IRQ_HANDLED;
+ }
+
+@@ -220,47 +212,37 @@ static void locomokbd_timer_callback(unsigned long data)
+ static int locomokbd_open(struct input_dev *dev)
+ {
+ struct locomokbd *locomokbd = input_get_drvdata(dev);
+- u16 r;
+-
+- r = locomo_readl(locomokbd->base + LOCOMO_KIC) | 0x0010;
+- locomo_writel(r, locomokbd->base + LOCOMO_KIC);
+- return 0;
++
++ return regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x10, 0x10);
+ }
+
+ static void locomokbd_close(struct input_dev *dev)
+ {
+ struct locomokbd *locomokbd = input_get_drvdata(dev);
+- u16 r;
+-
+- r = locomo_readl(locomokbd->base + LOCOMO_KIC) & ~0x0010;
+- locomo_writel(r, locomokbd->base + LOCOMO_KIC);
++
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x10, 0x0);
+ }
+
+-static int locomokbd_probe(struct locomo_dev *dev)
++static int locomokbd_probe(struct platform_device *dev)
+ {
+ struct locomokbd *locomokbd;
+ struct input_dev *input_dev;
+ int i, err;
+
+- locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
+- input_dev = input_allocate_device();
+- if (!locomokbd || !input_dev) {
+- err = -ENOMEM;
+- goto err_free_mem;
+- }
++ locomokbd = devm_kzalloc(&dev->dev, sizeof(struct locomokbd),
++ GFP_KERNEL);
++ if (!locomokbd)
++ return -ENOMEM;
+
+- /* try and claim memory region */
+- if (!request_mem_region((unsigned long) dev->mapbase,
+- dev->length,
+- LOCOMO_DRIVER_NAME(dev))) {
+- err = -EBUSY;
+- printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
+- goto err_free_mem;
+- }
++ locomokbd->regmap = dev_get_regmap(dev->dev.parent, NULL);
++ if (!locomokbd->regmap)
++ return -EINVAL;
+
+- locomo_set_drvdata(dev, locomokbd);
++ locomokbd->irq = platform_get_irq(dev, 0);
++ if (locomokbd->irq < 0)
++ return -ENXIO;
+
+- locomokbd->base = (unsigned long) dev->mapbase;
++ platform_set_drvdata(dev, locomokbd);
+
+ spin_lock_init(&locomokbd->lock);
+
+@@ -270,11 +252,13 @@ static int locomokbd_probe(struct locomo_dev *dev)
+
+ locomokbd->suspend_jiffies = jiffies;
+
+- locomokbd->input = input_dev;
+- strcpy(locomokbd->phys, "locomokbd/input0");
++ input_dev = input_allocate_device();
++ if (!input_dev)
++ return -ENOMEM;
+
++ locomokbd->input = input_dev;
+ input_dev->name = "LoCoMo keyboard";
+- input_dev->phys = locomokbd->phys;
++ input_dev->phys = "locomokbd/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+@@ -291,16 +275,30 @@ static int locomokbd_probe(struct locomo_dev *dev)
+
+ input_set_drvdata(input_dev, locomokbd);
+
+- memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
++ memcpy(locomokbd->keycode,
++ locomokbd_keycode,
++ sizeof(locomokbd->keycode));
++
++ if (machine_is_collie())
++ locomokbd->keycode[18] = KEY_HOME;
++ else
++ locomokbd->keycode[3] = KEY_HOME;
++
+ for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+- set_bit(locomokbd->keycode[i], input_dev->keybit);
+- clear_bit(0, input_dev->keybit);
++ input_set_capability(input_dev, EV_KEY, locomokbd->keycode[i]);
++ input_set_capability(input_dev, EV_PWR, KEY_SUSPEND);
++ __set_bit(EV_REP, input_dev->evbit);
++
++ regmap_write(locomokbd->regmap, LOCOMO_KCMD, 1);
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, 0x0);
++ regmap_write(locomokbd->regmap, LOCOMO_KIC, 0x0);
+
+ /* attempt to get the interrupt */
+- err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
++ err = request_irq(locomokbd->irq, locomokbd_interrupt, 0,
++ "locomokbd", locomokbd);
+ if (err) {
+- printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
+- goto err_release_region;
++ dev_err(&dev->dev, "locomokbd: Can't get irq for keyboard\n");
++ goto err_free_mem;
+ }
+
+ err = input_register_device(locomokbd->input);
+@@ -309,54 +307,71 @@ static int locomokbd_probe(struct locomo_dev *dev)
+
+ return 0;
+
+- err_free_irq:
+- free_irq(dev->irq[0], locomokbd);
+- err_release_region:
+- release_mem_region((unsigned long) dev->mapbase, dev->length);
+- locomo_set_drvdata(dev, NULL);
+- err_free_mem:
++err_free_irq:
++ free_irq(locomokbd->irq, locomokbd);
++err_free_mem:
+ input_free_device(input_dev);
+- kfree(locomokbd);
+
+ return err;
+ }
+
+-static int locomokbd_remove(struct locomo_dev *dev)
++static int locomokbd_remove(struct platform_device *dev)
+ {
+- struct locomokbd *locomokbd = locomo_get_drvdata(dev);
++ struct locomokbd *locomokbd = platform_get_drvdata(dev);
+
+- free_irq(dev->irq[0], locomokbd);
++ free_irq(locomokbd->irq, locomokbd);
+
+ del_timer_sync(&locomokbd->timer);
+
+ input_unregister_device(locomokbd->input);
+- locomo_set_drvdata(dev, NULL);
+
+- release_mem_region((unsigned long) dev->mapbase, dev->length);
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int locomokbd_suspend(struct device *dev)
++{
++ struct locomokbd *locomokbd = dev_get_drvdata(dev);
++
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x10, 0x0);
+
+- kfree(locomokbd);
++ del_timer_sync(&locomokbd->timer);
+
+ return 0;
+ }
+
+-static struct locomo_driver keyboard_driver = {
+- .drv = {
+- .name = "locomokbd"
++static int locomokbd_resume(struct device *dev)
++{
++ struct locomokbd *locomokbd = dev_get_drvdata(dev);
++
++ regmap_write(locomokbd->regmap, LOCOMO_KCMD, 1);
++ regmap_write(locomokbd->regmap, LOCOMO_KSC, 0);
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x100, 0);
++ regmap_update_bits(locomokbd->regmap, LOCOMO_KIC, 0x10, 0x10);
++
++ locomokbd_scankeyboard(locomokbd);
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(locomo_kbd_pm, locomokbd_suspend, locomokbd_resume);
++#define LOCOMO_KBD_PM (&locomo_kbd_pm)
++#else
++#define LOCOMO_KBD_PM NULL
++#endif
++
++static struct platform_driver locomokbd_driver = {
++ .driver = {
++ .name = "locomo-kbd",
++ .pm = LOCOMO_KBD_PM,
+ },
+- .devid = LOCOMO_DEVID_KEYBOARD,
+ .probe = locomokbd_probe,
+ .remove = locomokbd_remove,
+ };
+
+-static int __init locomokbd_init(void)
+-{
+- return locomo_driver_register(&keyboard_driver);
+-}
+-
+-static void __exit locomokbd_exit(void)
+-{
+- locomo_driver_unregister(&keyboard_driver);
+-}
++module_platform_driver(locomokbd_driver);
+
+-module_init(locomokbd_init);
+-module_exit(locomokbd_exit);
++MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
++MODULE_DESCRIPTION("LoCoMo keyboard driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:locomo-kbd");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0005-input-locomokbd-provide-an-Alt-SysRQ-combination.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0005-input-locomokbd-provide-an-Alt-SysRQ-combination.patch
new file mode 100644
index 0000000..6ccda75
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0005-input-locomokbd-provide-an-Alt-SysRQ-combination.patch
@@ -0,0 +1,75 @@
+From d947e63ae4890ac4ecd6f408f68dbcafdcc34482 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Wed, 3 Dec 2014 20:26:56 +0300
+Subject: [PATCH 05/20] input: locomokbd: provide an Alt-SysRQ combination
+
+It is usefull sometimes to have an Alt-SysRQ combo on the keyboard to be
+able to trigger sysrq functions directly. Add an option providing sysrq
+mapping for Contact-Home keys.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/input/keyboard/Kconfig | 6 ++++++
+ drivers/input/keyboard/locomokbd.c | 14 +++++++++++---
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index 4e20538..8d6d4a8 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -343,6 +343,12 @@ config KEYBOARD_LOCOMO
+ To compile this driver as a module, choose M here: the
+ module will be called locomokbd.
+
++config KEYBOARD_LOCOMOKBD_SYSRQ
++ bool "Provide SysRQ key on LoCoMo keyboard"
++ help
++ Say Y here to be able to use Contact-Home as Alt-Sysrq combo.
++ Say N if you want to use them as usual keys.
++
+ config KEYBOARD_LPC32XX
+ tristate "LPC32XX matrix key scanner support"
+ depends on ARCH_LPC32XX && OF
+diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
+index eed0a94..eb8dbe2 100644
+--- a/drivers/input/keyboard/locomokbd.c
++++ b/drivers/input/keyboard/locomokbd.c
+@@ -40,6 +40,14 @@
+ #define KEY_CONTACT KEY_F18
+ #define KEY_CENTER KEY_F15
+
++#ifdef CONFIG_KEYBOARD_LOCOMOKBD_SYSRQ
++#define LOCOMO_KEY_CONTACT KEY_LEFTALT
++#define LOCOMO_KEY_HOME KEY_SYSRQ
++#else
++#define LOCOMO_KEY_CONTACT KEY_CONTACT
++#define LOCOMO_KEY_HOME KEY_HOME
++#endif
++
+ #define KB_ROWS 16
+ #define KB_COLS 8
+ #define LOCOMOKBD_NUMKEYS (KB_ROWS * KB_COLS)
+@@ -48,7 +56,7 @@
+ static const unsigned char
+ locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+ 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
+- 0, 0, 0, 0, 0, 0, 0, KEY_MENU, 0, KEY_CONTACT, /* 10 - 19 */
++ 0, 0, 0, 0, 0, 0, 0, KEY_MENU, 0, LOCOMO_KEY_CONTACT, /* 10 - 19 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
+ 0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
+@@ -280,9 +288,9 @@ static int locomokbd_probe(struct platform_device *dev)
+ sizeof(locomokbd->keycode));
+
+ if (machine_is_collie())
+- locomokbd->keycode[18] = KEY_HOME;
++ locomokbd->keycode[18] = LOCOMO_KEY_HOME;
+ else
+- locomokbd->keycode[3] = KEY_HOME;
++ locomokbd->keycode[3] = LOCOMO_KEY_HOME;
+
+ for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+ input_set_capability(input_dev, EV_KEY, locomokbd->keycode[i]);
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0006-video-backlight-add-new-locomo-backlight-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0006-video-backlight-add-new-locomo-backlight-driver.patch
new file mode 100644
index 0000000..cf2ebe7
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0006-video-backlight-add-new-locomo-backlight-driver.patch
@@ -0,0 +1,212 @@
+From 3be6ca10a2c5275f6c89818b7478b21567dc3cf3 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 03:10:17 +0400
+Subject: [PATCH 06/20] video: backlight: add new locomo backlight driver
+
+Add new simple backlight driver - it cares only about PWM/frontlight
+part of LoCoMo, it does not touch TFT settings and does not export TFT
+power control.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/video/backlight/Kconfig | 6 +-
+ drivers/video/backlight/Makefile | 2 +-
+ drivers/video/backlight/locomo_bl.c | 153 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 157 insertions(+), 4 deletions(-)
+ create mode 100644 drivers/video/backlight/locomo_bl.c
+
+diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
+index efb0904..540ab64 100644
+--- a/drivers/video/backlight/Kconfig
++++ b/drivers/video/backlight/Kconfig
+@@ -217,12 +217,12 @@ config BACKLIGHT_LM3533
+ levels.
+
+ config BACKLIGHT_LOCOMO
+- tristate "Sharp LOCOMO LCD/Backlight Driver"
+- depends on SHARP_LOCOMO
++ tristate "Sharp LOCOMO Backlight Driver"
++ depends on MFD_LOCOMO
+ default y
+ help
+ If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to
+- enable the LCD/backlight driver.
++ enable the backlight driver.
+
+ config BACKLIGHT_OMAP1
+ tristate "OMAP1 PWL-based LCD Backlight"
+diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
+index fcd50b73..2a61b7e 100644
+--- a/drivers/video/backlight/Makefile
++++ b/drivers/video/backlight/Makefile
+@@ -39,7 +39,7 @@ obj-$(CONFIG_BACKLIGHT_IPAQ_MICRO) += ipaq_micro_bl.o
+ obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o
+ obj-$(CONFIG_BACKLIGHT_LM3630A) += lm3630a_bl.o
+ obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o
+-obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
++obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomo_bl.o
+ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
+ obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
+ obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
+diff --git a/drivers/video/backlight/locomo_bl.c b/drivers/video/backlight/locomo_bl.c
+new file mode 100644
+index 0000000..cc60022
+--- /dev/null
++++ b/drivers/video/backlight/locomo_bl.c
+@@ -0,0 +1,153 @@
++/*
++ * Backlight control code for Sharp Zaurus SL-5500
++ *
++ * Copyright 2005 John Lenz <lenz@cs.wisc.edu>
++ * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
++ * GPL v2
++ *
++ * This driver assumes single CPU. That's okay, because collie is
++ * slightly old hardware, and no one is going to retrofit second CPU to
++ * old PDA.
++ */
++
++#include <linux/backlight.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/gpio/consumer.h>
++#include <linux/mfd/locomo.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++struct locomo_bl {
++ struct regmap *regmap;
++ int current_intensity;
++ struct gpio_desc *fl_vr;
++};
++
++static const struct {
++ u16 duty, bpwf;
++ bool vr;
++} locomo_bl_pwm[] = {
++ { 0, 161, false },
++ { 117, 161, false },
++ { 163, 148, false },
++ { 194, 161, false },
++ { 194, 161, true },
++};
++
++static int locomo_bl_set_intensity(struct backlight_device *bd)
++{
++ int intensity = bd->props.brightness;
++ struct locomo_bl *bl = dev_get_drvdata(&bd->dev);
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.state & BL_CORE_SUSPENDED)
++ intensity = 0;
++
++ gpiod_set_value(bl->fl_vr, locomo_bl_pwm[intensity].vr);
++
++ regmap_write(bl->regmap, LOCOMO_ALS, locomo_bl_pwm[intensity].bpwf);
++ usleep_range(100, 200);
++ regmap_write(bl->regmap, LOCOMO_ALD, locomo_bl_pwm[intensity].duty);
++ usleep_range(100, 200);
++ regmap_write(bl->regmap, LOCOMO_ALS, locomo_bl_pwm[intensity].bpwf |
++ LOCOMO_ALS_EN);
++
++ bl->current_intensity = intensity;
++ if (bd->props.state & BL_CORE_SUSPENDED)
++ regmap_write(bl->regmap, LOCOMO_ALS, 0x00);
++
++ return 0;
++}
++
++static int locomo_bl_get_intensity(struct backlight_device *bd)
++{
++ struct locomo_bl *bl = dev_get_drvdata(&bd->dev);
++
++ return bl->current_intensity;
++}
++
++static const struct backlight_ops locomo_bl_ops = {
++ .options = BL_CORE_SUSPENDRESUME,
++ .get_brightness = locomo_bl_get_intensity,
++ .update_status = locomo_bl_set_intensity,
++};
++
++static int locomo_bl_probe(struct platform_device *dev)
++{
++ struct backlight_properties props;
++ struct locomo_bl *bl;
++ struct backlight_device *locomo_bl_device;
++
++ bl = devm_kmalloc(&dev->dev, sizeof(struct locomo_bl), GFP_KERNEL);
++ if (!bl)
++ return -ENOMEM;
++
++ bl->regmap = dev_get_regmap(dev->dev.parent, NULL);
++ if (!bl->regmap)
++ return -ENODEV;
++
++ bl->fl_vr = devm_gpiod_get(&dev->dev, "flvr", GPIOD_OUT_LOW);
++ if (IS_ERR(bl->fl_vr))
++ return PTR_ERR(bl->fl_vr);
++
++ regmap_write(bl->regmap, LOCOMO_ALS, 0);
++ regmap_write(bl->regmap, LOCOMO_ALD, 0);
++
++ memset(&props, 0, sizeof(struct backlight_properties));
++ props.type = BACKLIGHT_RAW;
++ props.max_brightness = ARRAY_SIZE(locomo_bl_pwm) - 1;
++ props.brightness = props.max_brightness / 2;
++ locomo_bl_device = devm_backlight_device_register(&dev->dev,
++ "locomo-bl",
++ &dev->dev, bl,
++ &locomo_bl_ops, &props);
++
++ if (IS_ERR(locomo_bl_device))
++ return PTR_ERR(locomo_bl_device);
++
++ platform_set_drvdata(dev, locomo_bl_device);
++
++ /* Set up frontlight so that screen is readable */
++ backlight_update_status(locomo_bl_device);
++
++ return 0;
++}
++
++static void locomo_bl_shutdown(struct platform_device *dev)
++{
++ struct backlight_device *locomo_bl_device = platform_get_drvdata(dev);
++
++ locomo_bl_device->props.brightness = 0;
++ locomo_bl_device->props.power = 0;
++ locomo_bl_set_intensity(locomo_bl_device);
++}
++
++static int locomo_bl_remove(struct platform_device *dev)
++{
++ locomo_bl_shutdown(dev);
++
++ return 0;
++}
++
++static struct platform_driver locomo_bl_driver = {
++ .driver = {
++ .name = "locomo-backlight",
++ },
++ .probe = locomo_bl_probe,
++ .remove = locomo_bl_remove,
++ /* Turn off bl on power off/reboot */
++ .shutdown = locomo_bl_shutdown,
++};
++
++module_platform_driver(locomo_bl_driver);
++
++MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
++MODULE_AUTHOR("Pavel Machek <pavel@ucw.cz>");
++MODULE_DESCRIPTION("LoCoMo Backlight driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:locomo-backlight");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0007-video-lcd-add-LoCoMo-LCD-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0007-video-lcd-add-LoCoMo-LCD-driver.patch
new file mode 100644
index 0000000..f9ea7eb
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0007-video-lcd-add-LoCoMo-LCD-driver.patch
@@ -0,0 +1,343 @@
+From b85af3e957277b30a2ef51d605478a292a4be817 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Wed, 4 Dec 2013 23:58:34 +0400
+Subject: [PATCH 07/20] video: lcd: add LoCoMo LCD driver
+
+LoCoMo has some special handling for TFT screens attached to Collie and
+Poodle. Implement that as a separate driver.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/video/backlight/Kconfig | 10 ++
+ drivers/video/backlight/Makefile | 1 +
+ drivers/video/backlight/locomo_lcd.c | 285 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 296 insertions(+)
+ create mode 100644 drivers/video/backlight/locomo_lcd.c
+
+diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
+index 540ab64..ba0f43c 100644
+--- a/drivers/video/backlight/Kconfig
++++ b/drivers/video/backlight/Kconfig
+@@ -48,6 +48,16 @@ config LCD_LMS283GF05
+ SPI driver for Samsung LMS283GF05. This provides basic support
+ for powering the LCD up/down through a sysfs interface.
+
++config LCD_LOCOMO
++ tristate "Sharp LOCOMO LCD Driver"
++ depends on MFD_LOCOMO
++ select IIO
++ help
++ If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to
++ enable the LCD driver. The panel starts up in power
++ off state, so you need this driver in order to see any
++ output.
++
+ config LCD_LTV350QV
+ tristate "Samsung LTV350QV LCD Panel"
+ depends on SPI_MASTER
+diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
+index 2a61b7e..bb360a2 100644
+--- a/drivers/video/backlight/Makefile
++++ b/drivers/video/backlight/Makefile
+@@ -11,6 +11,7 @@ obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o
+ obj-$(CONFIG_LCD_LD9040) += ld9040.o
+ obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o
+ obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o
++obj-$(CONFIG_LCD_LOCOMO) += locomo_lcd.o
+ obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+ obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
+ obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
+diff --git a/drivers/video/backlight/locomo_lcd.c b/drivers/video/backlight/locomo_lcd.c
+new file mode 100644
+index 0000000..dc316cb
+--- /dev/null
++++ b/drivers/video/backlight/locomo_lcd.c
+@@ -0,0 +1,285 @@
++/*
++ * Backlight control code for Sharp Zaurus SL-5500
++ *
++ * Copyright 2005 John Lenz <lenz@cs.wisc.edu>
++ * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
++ * GPL v2
++ *
++ * This driver assumes single CPU. That's okay, because collie is
++ * slightly old hardware, and no one is going to retrofit second CPU to
++ * old PDA.
++ */
++
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/gpio/consumer.h>
++#include <linux/iio/consumer.h>
++#include <linux/lcd.h>
++#include <linux/mfd/locomo.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++struct locomo_lcd {
++ struct regmap *regmap;
++ struct platform_device *dev;
++ struct locomo_lcd_platform_data *data;
++ int power;
++ struct iio_channel *comadj;
++ struct gpio_desc *vsha, *vshd, *vee, *mod;
++};
++
++static void locomo_lcd_on(struct locomo_lcd *lcd)
++{
++ gpiod_set_value(lcd->vsha, 1);
++ usleep_range(2000, 3000);
++
++ gpiod_set_value(lcd->vshd, 1);
++ usleep_range(2000, 3000);
++
++ iio_write_channel_raw(lcd->comadj, lcd->data->comadj);
++ usleep_range(5000, 6000);
++
++ gpiod_set_value(lcd->vee, 1);
++ usleep_range(10000, 11000);
++
++ /* TFTCRST | CPSOUT=0 | CPSEN */
++ regmap_write(lcd->regmap, LOCOMO_TC, 0x01);
++
++ /* Set CPSD */
++ regmap_write(lcd->regmap, LOCOMO_CPSD, 6);
++
++ /* TFTCRST | CPSOUT=0 | CPSEN */
++ regmap_write(lcd->regmap, LOCOMO_TC, 0x04 | 0x01);
++ usleep_range(10000, 11000);
++
++ gpiod_set_value(lcd->mod, 1);
++}
++
++static void locomo_lcd_off(struct locomo_lcd *lcd)
++{
++ /* TFTCRST=1 | CPSOUT=1 | CPSEN = 0 */
++ regmap_write(lcd->regmap, LOCOMO_TC, 0x06);
++ usleep_range(1000, 2000);
++
++ gpiod_set_value(lcd->vsha, 0);
++ msleep(110);
++
++ gpiod_set_value(lcd->vee, 0);
++ msleep(700);
++
++ iio_write_channel_raw(lcd->comadj, 0);
++ usleep_range(5000, 6000);
++
++ /* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
++ regmap_write(lcd->regmap, LOCOMO_TC, 0);
++ gpiod_set_value(lcd->mod, 0);
++ gpiod_set_value(lcd->vshd, 0);
++}
++
++static void locomo_lcd_program_adsync(struct locomo_lcd *lcd)
++{
++ regmap_write(lcd->regmap, LOCOMO_ASD,
++ 6 + 8 + 320 + 30 - 10);
++ regmap_update_bits(lcd->regmap, LOCOMO_ASD,
++ 0x8000,
++ 0x8000);
++
++ regmap_write(lcd->regmap, LOCOMO_HSD,
++ 6 + 8 + 320 + 30 - 10 - 128 + 4);
++ regmap_update_bits(lcd->regmap, LOCOMO_HSD,
++ 0x8000,
++ 0x8000);
++
++ regmap_write(lcd->regmap, LOCOMO_HSC, 128 / 8);
++
++ /* XON */
++ regmap_write(lcd->regmap, LOCOMO_TADC, 0x80);
++ usleep_range(1000, 1100);
++
++ /* CLK9MEN */
++ regmap_update_bits(lcd->regmap, LOCOMO_TADC,
++ 0x10,
++ 0x10);
++ usleep_range(100, 200);
++}
++
++static void locomo_lcd_disable_adsync(struct locomo_lcd *lcd)
++{
++ /* ADSTART */
++ regmap_write(lcd->regmap, LOCOMO_ASD, 0x00);
++
++ /* 18MHz clock off*/
++ regmap_write(lcd->regmap, LOCOMO_TADC, 0x00);
++}
++
++int locomo_lcd_set_power(struct lcd_device *ldev, int power)
++{
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ dev_dbg(&ldev->dev, "LCD power %d (is %d)\n", power, lcd->power);
++
++ if (!power && lcd->power)
++ locomo_lcd_on(lcd);
++
++ if (power && !lcd->power)
++ locomo_lcd_off(lcd);
++
++ lcd->power = power;
++
++ return 0;
++}
++
++static int locomo_lcd_get_power(struct lcd_device *ldev)
++{
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ return lcd->power;
++}
++
++static struct lcd_ops locomo_lcd_ops = {
++ .set_power = locomo_lcd_set_power,
++ .get_power = locomo_lcd_get_power,
++};
++
++#ifdef CONFIG_PM_SLEEP
++static int locomo_lcd_suspend(struct device *dev)
++{
++ struct lcd_device *ldev = dev_get_drvdata(dev);
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ locomo_lcd_off(lcd);
++
++ locomo_lcd_disable_adsync(lcd);
++
++ return 0;
++}
++
++static int locomo_lcd_resume(struct device *dev)
++{
++ struct lcd_device *ldev = dev_get_drvdata(dev);
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ locomo_lcd_program_adsync(lcd);
++
++ if (!lcd->power)
++ locomo_lcd_on(lcd);
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(locomo_lcd_pm, locomo_lcd_suspend, locomo_lcd_resume);
++#define LOCOMOLCD_PM (&locomo_lcd_pm)
++#else
++#define LOCOMOLCD_PM NULL
++#endif
++
++static int locomo_lcd_probe(struct platform_device *dev)
++{
++ struct lcd_device *lcd_dev;
++ struct locomo_lcd *lcd;
++ int rc;
++
++ lcd = devm_kmalloc(&dev->dev, sizeof(struct locomo_lcd), GFP_KERNEL);
++ if (!lcd)
++ return -ENOMEM;
++
++ lcd->dev = dev;
++ lcd->power = FB_BLANK_NORMAL;
++
++ lcd->regmap = dev_get_regmap(dev->dev.parent, NULL);
++ if (!lcd->regmap)
++ return -ENODEV;
++
++ lcd->data = dev_get_platdata(&dev->dev);
++ if (!lcd->data)
++ return -EINVAL;
++
++ lcd->vsha = devm_gpiod_get(&dev->dev, "VSHA", GPIOD_OUT_LOW);
++ if (IS_ERR(lcd->vsha))
++ return PTR_ERR(lcd->vsha);
++
++ lcd->vshd = devm_gpiod_get(&dev->dev, "VSHD", GPIOD_OUT_LOW);
++ if (IS_ERR(lcd->vshd))
++ return PTR_ERR(lcd->vshd);
++
++ lcd->vee = devm_gpiod_get(&dev->dev, "Vee", GPIOD_OUT_LOW);
++ if (IS_ERR(lcd->vee))
++ return PTR_ERR(lcd->vee);
++
++ lcd->mod = devm_gpiod_get(&dev->dev, "MOD", GPIOD_OUT_LOW);
++ if (IS_ERR(lcd->mod))
++ return PTR_ERR(lcd->mod);
++
++ lcd->comadj = iio_channel_get(&dev->dev, "comadj");
++ if (IS_ERR(lcd->comadj)) {
++ rc = PTR_ERR(lcd->comadj);
++ if (rc == -ENODEV)
++ rc = -EPROBE_DEFER;
++
++ return rc;
++ }
++
++ locomo_lcd_program_adsync(lcd);
++
++ lcd_dev = devm_lcd_device_register(&dev->dev, "locomo", &dev->dev, lcd,
++ &locomo_lcd_ops);
++ if (IS_ERR(lcd_dev)) {
++ rc = PTR_ERR(lcd_dev);
++ goto err;
++ }
++
++ platform_set_drvdata(dev, lcd_dev);
++
++ lcd_set_power(lcd_dev, FB_BLANK_UNBLANK);
++
++ return 0;
++
++err:
++ locomo_lcd_disable_adsync(lcd);
++ iio_channel_release(lcd->comadj);
++
++ return rc;
++}
++
++static int locomo_lcd_remove(struct platform_device *dev)
++{
++ struct lcd_device *ldev = platform_get_drvdata(dev);
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ locomo_lcd_off(lcd);
++
++ locomo_lcd_disable_adsync(lcd);
++
++ iio_channel_release(lcd->comadj);
++
++ return 0;
++}
++
++static void locomo_lcd_shutdown(struct platform_device *dev)
++{
++ struct lcd_device *ldev = platform_get_drvdata(dev);
++ struct locomo_lcd *lcd = lcd_get_data(ldev);
++
++ locomo_lcd_off(lcd);
++
++ locomo_lcd_disable_adsync(lcd);
++}
++
++static struct platform_driver locomo_lcd_driver = {
++ .driver = {
++ .name = "locomo-lcd",
++ .pm = LOCOMOLCD_PM,
++ },
++ .probe = locomo_lcd_probe,
++ .remove = locomo_lcd_remove,
++ .shutdown = locomo_lcd_shutdown,
++};
++
++module_platform_driver(locomo_lcd_driver);
++
++MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
++MODULE_AUTHOR("Pavel Machek <pavel@ucw.cz>");
++MODULE_DESCRIPTION("LoCoMo LCD driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:locomo-lcd");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0008-GPIO-port-LoCoMo-gpio-support-from-old-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0008-GPIO-port-LoCoMo-gpio-support-from-old-driver.patch
new file mode 100644
index 0000000..3f72520
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0008-GPIO-port-LoCoMo-gpio-support-from-old-driver.patch
@@ -0,0 +1,226 @@
+From d9847bd340045c31bfdde1fa91f319b3cfb196c0 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 03:03:21 +0400
+Subject: [PATCH 08/20] GPIO: port LoCoMo gpio support from old driver
+
+Add gpiolib driver for gpio pins placed on the LoCoMo GA.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/gpio/Kconfig | 9 +++
+ drivers/gpio/Makefile | 1 +
+ drivers/gpio/gpio-locomo.c | 170 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 180 insertions(+)
+ create mode 100644 drivers/gpio/gpio-locomo.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index c1e2ca3..9b1c2e6 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -504,6 +504,15 @@ config GPIO_TB10X
+ select GENERIC_IRQ_CHIP
+ select OF_GPIO
+
++config GPIO_LOCOMO
++ bool "Sharp LoCoMo GPIO support"
++ depends on MFD_LOCOMO
++ help
++ Select this to support GPIO pins on Sharp LoCoMo Grid Array found
++ in Sharp Zaurus collie and poodle models.
++
++ Sat Yes if you have such PDA, say No otherwise.
++
+ comment "I2C GPIO expanders:"
+
+ config GPIO_ARIZONA
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index bdda6a9..0a97e98 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -41,6 +41,7 @@ obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
+ obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
+ obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
+ obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
++obj-$(CONFIG_GPIO_LOCOMO) += gpio-locomo.o
+ obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
+ obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
+ obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
+diff --git a/drivers/gpio/gpio-locomo.c b/drivers/gpio/gpio-locomo.c
+new file mode 100644
+index 0000000..dd9a1ca
+--- /dev/null
++++ b/drivers/gpio/gpio-locomo.c
+@@ -0,0 +1,170 @@
++/*
++ * Sharp LoCoMo support for GPIO
++ *
++ * 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.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/bitops.h>
++#include <linux/err.h>
++#include <linux/gpio.h>
++#include <linux/io.h>
++#include <linux/regmap.h>
++#include <linux/mfd/locomo.h>
++
++struct locomo_gpio {
++ struct regmap *regmap;
++
++ struct gpio_chip gpio;
++
++ u16 rising_edge;
++ u16 falling_edge;
++
++ unsigned int save_gpo;
++ unsigned int save_gpe;
++};
++
++static int locomo_gpio_get(struct gpio_chip *chip,
++ unsigned offset)
++{
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ unsigned int gpl;
++
++ regmap_read(lg->regmap, LOCOMO_GPL, &gpl);
++
++ return gpl & BIT(offset);
++}
++
++static void locomo_gpio_set(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GPO,
++ BIT(offset),
++ value ? BIT(offset) : 0);
++}
++
++static int locomo_gpio_direction_input(struct gpio_chip *chip,
++ unsigned offset)
++{
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GPD, BIT(offset), BIT(offset));
++ regmap_update_bits(lg->regmap, LOCOMO_GPE, BIT(offset), BIT(offset));
++
++ return 0;
++}
++
++static int locomo_gpio_direction_output(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GPO,
++ BIT(offset),
++ value ? BIT(offset) : 0);
++ regmap_update_bits(lg->regmap, LOCOMO_GPD, BIT(offset), 0);
++ regmap_update_bits(lg->regmap, LOCOMO_GPE, BIT(offset), 0);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int locomo_gpio_suspend(struct device *dev)
++{
++ struct locomo_gpio *lg = dev_get_drvdata(dev);
++
++ regmap_read(lg->regmap, LOCOMO_GPO, &lg->save_gpo);
++ regmap_write(lg->regmap, LOCOMO_GPO, 0x00);
++ regmap_read(lg->regmap, LOCOMO_GPE, &lg->save_gpe);
++ regmap_write(lg->regmap, LOCOMO_GPE, 0x00);
++
++ return 0;
++}
++
++static int locomo_gpio_resume(struct device *dev)
++{
++ struct locomo_gpio *lg = dev_get_drvdata(dev);
++
++ regmap_write(lg->regmap, LOCOMO_GPO, lg->save_gpo);
++ regmap_write(lg->regmap, LOCOMO_GPE, lg->save_gpe);
++
++ return 0;
++}
++static SIMPLE_DEV_PM_OPS(locomo_gpio_pm,
++ locomo_gpio_suspend, locomo_gpio_resume);
++#define LOCOMO_GPIO_PM (&locomo_gpio_pm)
++#else
++#define LOCOMO_GPIO_PM NULL
++#endif
++
++static int locomo_gpio_probe(struct platform_device *pdev)
++{
++ struct locomo_gpio *lg;
++ int ret;
++ struct locomo_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
++
++ lg = devm_kzalloc(&pdev->dev, sizeof(struct locomo_gpio),
++ GFP_KERNEL);
++ if (!lg)
++ return -ENOMEM;
++
++ lg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
++ if (!lg->regmap)
++ return -EINVAL;
++
++ platform_set_drvdata(pdev, lg);
++
++ regmap_write(lg->regmap, LOCOMO_GPO, 0x00);
++ regmap_write(lg->regmap, LOCOMO_GPE, 0x00);
++ regmap_write(lg->regmap, LOCOMO_GPD, 0x00);
++ regmap_write(lg->regmap, LOCOMO_GIE, 0x00);
++
++ lg->gpio.base = pdata ? pdata->gpio_base : -1;
++ lg->gpio.label = "locomo-gpio";
++ lg->gpio.ngpio = 16;
++ lg->gpio.set = locomo_gpio_set;
++ lg->gpio.get = locomo_gpio_get;
++ lg->gpio.direction_input = locomo_gpio_direction_input;
++ lg->gpio.direction_output = locomo_gpio_direction_output;
++
++ ret = gpiochip_add(&lg->gpio);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int locomo_gpio_remove(struct platform_device *pdev)
++{
++ struct locomo_gpio *lg = platform_get_drvdata(pdev);
++
++ gpiochip_remove(&lg->gpio);
++
++ return 0;
++}
++
++static struct platform_driver locomo_gpio_driver = {
++ .probe = locomo_gpio_probe,
++ .remove = locomo_gpio_remove,
++ .driver = {
++ .name = "locomo-gpio",
++ .pm = LOCOMO_GPIO_PM,
++ },
++};
++module_platform_driver(locomo_gpio_driver);
++
++MODULE_DESCRIPTION("Sharp LoCoMo GPIO driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
++MODULE_ALIAS("platform:locomo-gpio");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0009-gpio-locomo-implement-per-pin-irq-handling.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0009-gpio-locomo-implement-per-pin-irq-handling.patch
new file mode 100644
index 0000000..7d07659
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0009-gpio-locomo-implement-per-pin-irq-handling.patch
@@ -0,0 +1,204 @@
+From ec2485ae9fd5238d1def04e6377d213285ab3315 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 04:27:27 +0400
+Subject: [PATCH 09/20] gpio: locomo: implement per-pin irq handling
+
+LoCoMo has a possibility to generate per-GPIO edge irqs. Support for
+that was there in old locomo driver, got 'cleaned up' during old driver
+IRQ cascading cleanup and is now reimplemented. It is expected that
+SL-5500 (collie) will use locomo gpio irqs for mmc detection irq.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/gpio/Kconfig | 1 +
+ drivers/gpio/gpio-locomo.c | 120 ++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 120 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 9b1c2e6..b6ebcb6 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -507,6 +507,7 @@ config GPIO_TB10X
+ config GPIO_LOCOMO
+ bool "Sharp LoCoMo GPIO support"
+ depends on MFD_LOCOMO
++ select GPIOLIB_IRQCHIP
+ help
+ Select this to support GPIO pins on Sharp LoCoMo Grid Array found
+ in Sharp Zaurus collie and poodle models.
+diff --git a/drivers/gpio/gpio-locomo.c b/drivers/gpio/gpio-locomo.c
+index dd9a1ca..d8e5880 100644
+--- a/drivers/gpio/gpio-locomo.c
++++ b/drivers/gpio/gpio-locomo.c
+@@ -16,13 +16,15 @@
+ #include <linux/slab.h>
+ #include <linux/bitops.h>
+ #include <linux/err.h>
+-#include <linux/gpio.h>
++#include <linux/gpio/driver.h>
+ #include <linux/io.h>
++#include <linux/irq.h>
+ #include <linux/regmap.h>
+ #include <linux/mfd/locomo.h>
+
+ struct locomo_gpio {
+ struct regmap *regmap;
++ int irq;
+
+ struct gpio_chip gpio;
+
+@@ -79,6 +81,99 @@ static int locomo_gpio_direction_output(struct gpio_chip *chip,
+ return 0;
+ }
+
++static void
++locomo_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
++{
++ struct gpio_chip *chip = irq_get_handler_data(irq);
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ struct irq_chip *irqchip = irq_desc_get_chip(desc);
++ unsigned int gir;
++ unsigned int gpd;
++ unsigned int req;
++
++ chained_irq_enter(irqchip, desc);
++
++ while (1) {
++ regmap_read(lg->regmap, LOCOMO_GIR, &gir);
++ regmap_read(lg->regmap, LOCOMO_GPD, &gpd);
++ req = gir & gpd;
++
++ if (!req)
++ break;
++
++ generic_handle_irq(irq_find_mapping(lg->gpio.irqdomain,
++ ffs(req) - 1));
++ }
++
++ chained_irq_exit(irqchip, desc);
++}
++
++static void locomo_gpio_ack_irq(struct irq_data *d)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ unsigned int mask = BIT(d->hwirq);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GWE, mask, mask);
++ regmap_update_bits(lg->regmap, LOCOMO_GIS, mask, 0);
++ regmap_update_bits(lg->regmap, LOCOMO_GWE, mask, 0);
++}
++
++static void locomo_gpio_mask_irq(struct irq_data *d)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ unsigned int mask = BIT(d->hwirq);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GIE, mask, 0);
++}
++
++static void locomo_gpio_unmask_irq(struct irq_data *d)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ unsigned int mask = BIT(d->hwirq);
++
++ regmap_update_bits(lg->regmap, LOCOMO_GIE, mask, mask);
++}
++
++static int locomo_gpio_type_irq(struct irq_data *d, unsigned int type)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
++ struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
++ unsigned int mask;
++
++ mask = BIT(d->hwirq);
++
++ if (type == IRQ_TYPE_PROBE) {
++ if ((lg->rising_edge | lg->falling_edge) & mask)
++ return 0;
++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
++ }
++
++ if (type & IRQ_TYPE_EDGE_RISING)
++ lg->rising_edge |= mask;
++ else
++ lg->rising_edge &= ~mask;
++ if (type & IRQ_TYPE_EDGE_FALLING)
++ lg->falling_edge |= mask;
++ else
++ lg->falling_edge &= ~mask;
++
++ regmap_write(lg->regmap, LOCOMO_GRIE, lg->rising_edge);
++ regmap_write(lg->regmap, LOCOMO_GFIE, lg->falling_edge);
++
++ return 0;
++}
++
++static struct irq_chip locomo_gpio_irq_chip = {
++ .name = "LOCOMO-g",
++ .irq_ack = locomo_gpio_ack_irq,
++ .irq_mask = locomo_gpio_mask_irq,
++ .irq_unmask = locomo_gpio_unmask_irq,
++ .irq_set_type = locomo_gpio_type_irq,
++};
++
+ #ifdef CONFIG_PM_SLEEP
+ static int locomo_gpio_suspend(struct device *dev)
+ {
+@@ -119,6 +214,10 @@ static int locomo_gpio_probe(struct platform_device *pdev)
+ if (!lg)
+ return -ENOMEM;
+
++ lg->irq = platform_get_irq(pdev, 0);
++ if (lg->irq < 0)
++ return -ENXIO;
++
+ lg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!lg->regmap)
+ return -EINVAL;
+@@ -130,6 +229,7 @@ static int locomo_gpio_probe(struct platform_device *pdev)
+ regmap_write(lg->regmap, LOCOMO_GPD, 0x00);
+ regmap_write(lg->regmap, LOCOMO_GIE, 0x00);
+
++ lg->gpio.dev = &pdev->dev;
+ lg->gpio.base = pdata ? pdata->gpio_base : -1;
+ lg->gpio.label = "locomo-gpio";
+ lg->gpio.ngpio = 16;
+@@ -142,7 +242,22 @@ static int locomo_gpio_probe(struct platform_device *pdev)
+ if (ret)
+ return ret;
+
++ ret = gpiochip_irqchip_add(&lg->gpio, &locomo_gpio_irq_chip, 0,
++ handle_level_irq, IRQ_TYPE_NONE);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to add irq chip\n");
++ goto err_rm_gpiochip;
++ }
++
++ gpiochip_set_chained_irqchip(&lg->gpio, &locomo_gpio_irq_chip, lg->irq,
++ locomo_gpio_irq_handler);
++
+ return 0;
++
++err_rm_gpiochip:
++ gpiochip_remove(&lg->gpio);
++
++ return ret;
+ }
+
+ static int locomo_gpio_remove(struct platform_device *pdev)
+@@ -151,6 +266,9 @@ static int locomo_gpio_remove(struct platform_device *pdev)
+
+ gpiochip_remove(&lg->gpio);
+
++ irq_set_chained_handler(lg->irq, NULL);
++ irq_set_handler_data(lg->irq, NULL);
++
+ return 0;
+ }
+
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0010-spi-add-locomo-SPI-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0010-spi-add-locomo-SPI-driver.patch
new file mode 100644
index 0000000..94a91a4
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0010-spi-add-locomo-SPI-driver.patch
@@ -0,0 +1,421 @@
+From a6c75627194e087467ed57a9e50168375e8a5908 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Thu, 28 Nov 2013 15:47:18 +0400
+Subject: [PATCH 10/20] spi: add locomo SPI driver
+
+LoCoMo chip has a built-in simple SPI controller. On Sharp SL-5500 PDDAs
+it is connected to external MMC slot.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/spi/Kconfig | 10 ++
+ drivers/spi/Makefile | 1 +
+ drivers/spi/spi-locomo.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 374 insertions(+)
+ create mode 100644 drivers/spi/spi-locomo.c
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index ab8dfbe..96312d6 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -259,6 +259,16 @@ config SPI_LM70_LLP
+ which interfaces to an LM70 temperature sensor using
+ a parallel port.
+
++config SPI_LOCOMO
++ tristate "Locomo SPI master"
++ depends on MFD_LOCOMO
++ help
++ This enables using the SPI controller as present in the LoCoMo
++ chips. It is probably only useful on the Sharp SL-5x00 PDA family.
++
++ On SL-5500 and SL-5000 devices this controller is used for
++ MMC/SD cards.
++
+ config SPI_MPC52xx
+ tristate "Freescale MPC52xx SPI (non-PSC) controller support"
+ depends on PPC_MPC52xx
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index d8cbf65..623c463 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
+ obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
+ obj-$(CONFIG_SPI_IMX) += spi-imx.o
+ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
++obj-$(CONFIG_SPI_LOCOMO) += spi-locomo.o
+ obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
+ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
+ obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
+diff --git a/drivers/spi/spi-locomo.c b/drivers/spi/spi-locomo.c
+new file mode 100644
+index 0000000..71b36fd
+--- /dev/null
++++ b/drivers/spi/spi-locomo.c
+@@ -0,0 +1,363 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#include <linux/delay.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/spi/spi.h>
++#include <linux/mfd/locomo.h>
++
++struct locomospi_dev {
++ struct regmap *regmap;
++
++ int clock_base;
++ int clock_div;
++ unsigned nsecs;
++
++ unsigned int save_ct;
++ unsigned int save_md;
++};
++
++static int locomospi_reg_open(struct locomospi_dev *spidev)
++{
++ regmap_write(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_MSB1ST | LOCOMO_SPIMD_DOSTAT |
++ LOCOMO_SPIMD_RCPOL | LOCOMO_SPIMD_TCPOL |
++ (spidev->clock_base << 3) | spidev->clock_div);
++
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_XON,
++ LOCOMO_SPIMD_XON);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_XEN,
++ LOCOMO_SPIMD_XEN);
++
++ regmap_write(spidev->regmap, LOCOMO_SPICT, LOCOMO_SPICT_CS);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPICT,
++ LOCOMO_SPICT_CEN | LOCOMO_SPICT_RXUEN |
++ LOCOMO_SPICT_ALIGNEN,
++ LOCOMO_SPICT_CEN | LOCOMO_SPICT_RXUEN |
++ LOCOMO_SPICT_ALIGNEN);
++
++ usleep_range(200, 300);
++
++ regmap_update_bits(spidev->regmap, LOCOMO_SPICT, LOCOMO_SPICT_CS, 0);
++
++ return 0;
++}
++
++static int locomospi_reg_release(struct locomospi_dev *spidev)
++{
++ regmap_update_bits(spidev->regmap, LOCOMO_SPICT, LOCOMO_SPICT_CEN, 0);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD, LOCOMO_SPIMD_XEN, 0);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD, LOCOMO_SPIMD_XON, 0);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPICT,
++ LOCOMO_SPIMD_XEN,
++ LOCOMO_SPIMD_XEN);
++
++ return 0;
++}
++
++
++static void locomospi_chipselect(struct spi_device *spi, bool enable)
++{
++ struct locomospi_dev *spidev;
++
++ dev_dbg(&spi->dev, "SPI cs: %s\n", enable ? "enable" : "disable");
++
++ spidev = spi_master_get_devdata(spi->master);
++
++ regmap_update_bits(spidev->regmap, LOCOMO_SPICT, LOCOMO_SPICT_CS,
++ enable ? LOCOMO_SPICT_CS : 0);
++}
++
++static u32 locomospi_txrx_word(struct spi_device *spi,
++ unsigned nsecs,
++ u32 word)
++{
++ struct locomospi_dev *spidev;
++ int wait;
++ int j;
++ unsigned int rx;
++ unsigned int r;
++
++ spidev = spi_master_get_devdata(spi->master);
++
++ if (spidev->clock_div == DIV_64)
++ wait = 0x10000;
++ else
++ wait = 8;
++
++ for (j = 0; j < wait; j++) {
++ regmap_read(spidev->regmap, LOCOMO_SPIST, &r);
++ if (r & LOCOMO_SPI_RFW)
++ break;
++ }
++ if (j == wait)
++ dev_err(&spi->dev, "rfw timeout\n");
++
++ regmap_write(spidev->regmap, LOCOMO_SPITD, word);
++ ndelay(nsecs);
++
++ for (j = 0; j < wait; j++) {
++ regmap_read(spidev->regmap, LOCOMO_SPIST, &r);
++ if (r & LOCOMO_SPI_RFR)
++ break;
++ }
++ if (j == wait)
++ dev_err(&spi->dev, "rfr timeout\n");
++
++ regmap_read(spidev->regmap, LOCOMO_SPIRD, &rx);
++ ndelay(nsecs);
++
++ dev_dbg(&spi->dev, "SPI txrx: %02x/%02x\n", word, rx);
++
++ return rx;
++}
++
++static void locomo_spi_set_speed(struct locomospi_dev *spidev, u32 hz)
++{
++ spidev->nsecs = (1000000000/2) / hz;
++
++ if (hz >= 24576000) {
++ spidev->clock_base = CLOCK_25MHZ;
++ spidev->clock_div = DIV_1;
++ } else if (hz >= 22579200) {
++ spidev->clock_base = CLOCK_22MHZ;
++ spidev->clock_div = DIV_1;
++ } else if (hz >= 18432000) {
++ spidev->clock_base = CLOCK_18MHZ;
++ spidev->clock_div = DIV_1;
++ } else if (hz >= 12288000) {
++ spidev->clock_base = CLOCK_25MHZ;
++ spidev->clock_div = DIV_2;
++ } else if (hz >= 11289600) {
++ spidev->clock_base = CLOCK_22MHZ;
++ spidev->clock_div = DIV_2;
++ } else if (hz >= 9216000) {
++ spidev->clock_base = CLOCK_18MHZ;
++ spidev->clock_div = DIV_2;
++ } else if (hz >= 6144000) {
++ spidev->clock_base = CLOCK_25MHZ;
++ spidev->clock_div = DIV_4;
++ } else if (hz >= 5644800) {
++ spidev->clock_base = CLOCK_22MHZ;
++ spidev->clock_div = DIV_4;
++ } else if (hz >= 4608000) {
++ spidev->clock_base = CLOCK_18MHZ;
++ spidev->clock_div = DIV_4;
++ } else if (hz >= 3072000) {
++ spidev->clock_base = CLOCK_25MHZ;
++ spidev->clock_div = DIV_8;
++ } else if (hz >= 2822400) {
++ spidev->clock_base = CLOCK_22MHZ;
++ spidev->clock_div = DIV_8;
++ } else if (hz >= 2304000) {
++ spidev->clock_base = CLOCK_18MHZ;
++ spidev->clock_div = DIV_8;
++ } else if (hz >= 384000) {
++ spidev->clock_base = CLOCK_25MHZ;
++ spidev->clock_div = DIV_64;
++ } else if (hz >= 352800) {
++ spidev->clock_base = CLOCK_22MHZ;
++ spidev->clock_div = DIV_64;
++ } else { /* set to 288 Khz */
++ spidev->clock_base = CLOCK_18MHZ;
++ spidev->clock_div = DIV_64;
++ }
++
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_XSEL | LOCOMO_SPIMD_CLKSEL |
++ LOCOMO_SPIMD_XEN,
++ 0);
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_XSEL | LOCOMO_SPIMD_CLKSEL |
++ LOCOMO_SPIMD_XEN,
++ spidev->clock_div | (spidev->clock_base << 3) |
++ LOCOMO_SPIMD_XEN);
++
++ usleep_range(300, 400);
++}
++
++static int locomo_spi_setup_transfer(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct locomospi_dev *spidev;
++ u32 hz = 0;
++
++ if (t)
++ hz = t->speed_hz;
++ if (!hz)
++ hz = spi->max_speed_hz;
++
++ spidev = spi_master_get_devdata(spi->master);
++
++ regmap_update_bits(spidev->regmap, LOCOMO_SPIMD,
++ LOCOMO_SPIMD_XON,
++ hz ? LOCOMO_SPIMD_XON : 0);
++
++ if (hz != 0)
++ locomo_spi_set_speed(spidev, hz);
++
++ return 0;
++}
++
++static int locomospi_transfer_one(struct spi_master *master,
++ struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct locomospi_dev *spidev = spi_master_get_devdata(spi->master);
++ int rc;
++ unsigned count;
++ const u8 *tx = t->tx_buf;
++ u8 *rx = t->rx_buf;
++
++ if (!tx && !rx && t->len)
++ return -EINVAL;
++
++ rc = locomo_spi_setup_transfer(spi, t);
++ if (rc < 0)
++ return rc;
++
++ if (!t->len)
++ return 0;
++
++ for (count = t->len; likely(count > 0); count--) {
++ u8 word = 0;
++
++ if (tx)
++ word = *tx++;
++ word = locomospi_txrx_word(spi, spidev->nsecs, word);
++
++ if (rx)
++ *rx++ = word;
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int locomo_spi_suspend(struct device *dev)
++{
++ struct spi_master *master = dev_get_drvdata(dev);
++ struct locomospi_dev *spidev = spi_master_get_devdata(master);
++ int ret;
++
++ /* Stop the queue running */
++ ret = spi_master_suspend(master);
++ if (ret) {
++ dev_warn(dev, "cannot suspend master\n");
++ return ret;
++ }
++
++ regmap_read(spidev->regmap, LOCOMO_SPICT, &spidev->save_ct);
++ regmap_write(spidev->regmap, LOCOMO_SPICT, LOCOMO_SPICT_CS);
++ regmap_read(spidev->regmap, LOCOMO_SPIMD, &spidev->save_ct);
++ regmap_write(spidev->regmap, LOCOMO_SPIMD, 0x3c14);
++
++
++ return 0;
++}
++
++static int locomo_spi_resume(struct device *dev)
++{
++ struct spi_master *master = dev_get_drvdata(dev);
++ struct locomospi_dev *spidev = spi_master_get_devdata(master);
++ int ret;
++
++ regmap_write(spidev->regmap, LOCOMO_SPICT, spidev->save_ct);
++ regmap_write(spidev->regmap, LOCOMO_SPIMD, spidev->save_md);
++
++ /* Start the queue running */
++ ret = spi_master_resume(master);
++ if (ret)
++ dev_err(dev, "problem starting queue (%d)\n", ret);
++
++ return ret;
++}
++
++static SIMPLE_DEV_PM_OPS(locomo_spi_pm_ops,
++ locomo_spi_suspend, locomo_spi_resume);
++
++#define LOCOMO_SPI_PM_OPS (&locomo_spi_pm_ops)
++#else
++#define LOCOMO_SPI_PM_OPS NULL
++#endif
++
++static int locomo_spi_probe(struct platform_device *pdev)
++{
++ struct spi_master *master;
++ struct locomospi_dev *spidev;
++ int ret = -ENODEV;
++
++ master = spi_alloc_master(&pdev->dev, sizeof(struct locomospi_dev));
++ if (!master)
++ return -ENOMEM;
++
++ master->bus_num = 0;
++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
++ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 8);
++ master->max_speed_hz = 24576000;
++ master->num_chipselect = 1;
++ master->set_cs = locomospi_chipselect;
++ master->transfer_one = locomospi_transfer_one;
++
++ spidev = spi_master_get_devdata(master);
++
++ spidev->regmap = dev_get_regmap(pdev->dev.parent, NULL);
++ if (!spidev->regmap)
++ goto out_put;
++
++ spidev->clock_div = DIV_64;
++ spidev->clock_base = CLOCK_18MHZ;
++
++ platform_set_drvdata(pdev, master);
++
++ ret = locomospi_reg_open(spidev);
++ if (ret < 0)
++ goto out_put;
++
++ ret = devm_spi_register_master(&pdev->dev, master);
++ if (ret) {
++ dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
++ goto out_put;
++ }
++
++ return 0;
++
++out_put:
++ spi_master_put(master);
++ return ret;
++}
++
++static int locomo_spi_remove(struct platform_device *pdev)
++{
++ struct spi_master *master = platform_get_drvdata(pdev);
++ struct locomospi_dev *spidev = spi_master_get_devdata(master);
++
++ return locomospi_reg_release(spidev);
++}
++
++static struct platform_driver locomo_spi_driver = {
++ .probe = locomo_spi_probe,
++ .remove = locomo_spi_remove,
++ .driver = {
++ .name = "locomo-spi",
++ .pm = LOCOMO_SPI_PM_OPS,
++ },
++};
++module_platform_driver(locomo_spi_driver);
++
++MODULE_AUTHOR("Thomas Kunze thommy@tabao.de");
++MODULE_DESCRIPTION("LoCoMo SPI driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:locomo-spi");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0011-i2c-add-locomo-i2c-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0011-i2c-add-locomo-i2c-driver.patch
new file mode 100644
index 0000000..43a2bd3
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0011-i2c-add-locomo-i2c-driver.patch
@@ -0,0 +1,196 @@
+From 37279f73cbea78da52a4eac4a4c13406ff5500da Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Fri, 14 Nov 2014 16:07:58 +0300
+Subject: [PATCH 11/20] i2c: add locomo i2c driver
+
+LoCoMo chip contains a tiny i2c controller destined to control
+M62332 DAC. Provide a separate I2C driver for this cell.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/i2c/busses/Kconfig | 12 ++++
+ drivers/i2c/busses/Makefile | 1 +
+ drivers/i2c/busses/i2c-locomo.c | 136 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 149 insertions(+)
+ create mode 100644 drivers/i2c/busses/i2c-locomo.c
+
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index 22da9c2..a4b20ba 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -584,6 +584,18 @@ config I2C_KEMPLD
+ This driver can also be built as a module. If so, the module
+ will be called i2c-kempld.
+
++config I2C_LOCOMO
++ tristate "I2C bus support for LoCoMo chips"
++ depends on MFD_LOCOMO
++ select I2C_ALGOBIT
++ help
++ Say yes if you will run the kernel on Sharp SL-5x00 family of devices.
++
++ If you don't know what to do here, say N.
++
++ This driver can also be built as a module. If so, the module
++ will be called i2c-locomo.
++
+ config I2C_MESON
+ tristate "Amlogic Meson I2C controller"
+ depends on ARCH_MESON
+diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
+index 3638feb..d31ae1a 100644
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -56,6 +56,7 @@ obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
+ obj-$(CONFIG_I2C_IMX) += i2c-imx.o
+ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
+ obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
++obj-$(CONFIG_I2C_LOCOMO) += i2c-locomo.o
+ obj-$(CONFIG_I2C_MESON) += i2c-meson.o
+ obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
+ obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
+diff --git a/drivers/i2c/busses/i2c-locomo.c b/drivers/i2c/busses/i2c-locomo.c
+new file mode 100644
+index 0000000..640b46cd
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-locomo.c
+@@ -0,0 +1,136 @@
++/*
++ * 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.
++ */
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/mfd/locomo.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++struct locomo_i2c {
++ struct regmap *regmap;
++ struct i2c_adapter adap;
++ struct i2c_algo_bit_data bit;
++};
++
++static void locomo_i2c_setsda(void *data, int state)
++{
++ struct locomo_i2c *li2c = data;
++
++ regmap_update_bits(li2c->regmap, LOCOMO_DAC,
++ LOCOMO_DAC_SDAOEB,
++ state ? LOCOMO_DAC_SDAOEB : 0);
++}
++
++static void locomo_i2c_setscl(void *data, int state)
++{
++ struct locomo_i2c *li2c = data;
++
++ regmap_update_bits(li2c->regmap, LOCOMO_DAC,
++ LOCOMO_DAC_SCLOEB,
++ state ? LOCOMO_DAC_SCLOEB : 0);
++}
++
++static int locomo_i2c_getsda(void *data)
++{
++ struct locomo_i2c *li2c = data;
++ unsigned int r;
++
++ regmap_read(li2c->regmap, LOCOMO_DAC, &r);
++
++ return !!(r & LOCOMO_DAC_SDA);
++}
++
++static int locomo_i2c_probe(struct platform_device *dev)
++{
++ struct locomo_i2c *li2c;
++ int ret;
++
++ li2c = devm_kzalloc(&dev->dev, sizeof(struct locomo_i2c), GFP_KERNEL);
++ if (li2c == NULL)
++ return -ENOMEM;
++
++ li2c->regmap = dev_get_regmap(dev->dev.parent, NULL);
++ if (!li2c->regmap)
++ return -ENODEV;
++
++ li2c->adap.owner = THIS_MODULE;
++ li2c->adap.dev.parent = &dev->dev;
++ li2c->adap.dev.of_node = dev->dev.of_node;
++ li2c->adap.algo_data = &li2c->bit;
++ li2c->adap.nr = 1; /* On poodle, 0 is pxa internal bus */
++
++ strlcpy(li2c->adap.name, "LoCoMo I2C", sizeof(li2c->adap.name));
++
++ li2c->bit.data = li2c;
++ li2c->bit.setsda = locomo_i2c_setsda;
++ li2c->bit.setscl = locomo_i2c_setscl;
++ li2c->bit.getsda = locomo_i2c_getsda;
++ li2c->bit.udelay = 6;
++ li2c->bit.timeout = HZ;
++
++ ret = i2c_bit_add_numbered_bus(&li2c->adap);
++ if (ret)
++ return ret;
++
++ platform_set_drvdata(dev, li2c);
++
++ return 0;
++}
++
++static int locomo_i2c_remove(struct platform_device *dev)
++{
++ struct locomo_i2c *li2c = platform_get_drvdata(dev);
++
++ i2c_del_adapter(&li2c->adap);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int locomo_i2c_suspend(struct device *dev)
++{
++ struct locomo_i2c *li2c = dev_get_drvdata(dev);
++
++ regmap_write(li2c->regmap, LOCOMO_DAC, 0x00);
++
++ return 0;
++}
++
++static int locomo_i2c_resume(struct device *dev)
++{
++ struct locomo_i2c *li2c = dev_get_drvdata(dev);
++
++ regmap_write(li2c->regmap, LOCOMO_DAC,
++ LOCOMO_DAC_SDAOEB | LOCOMO_DAC_SCLOEB);
++
++ return 0;
++}
++static SIMPLE_DEV_PM_OPS(locomo_i2c_pm, locomo_i2c_suspend, locomo_i2c_resume);
++#define LOCOMO_I2C_PM (&locomo_i2c_pm)
++#else
++#define LOCOMO_I2C_PM NULL
++#endif
++
++
++static struct platform_driver locomo_i2c_driver = {
++ .driver = {
++ .name = "locomo-i2c",
++ .pm = LOCOMO_I2C_PM,
++ },
++ .probe = locomo_i2c_probe,
++ .remove = locomo_i2c_remove,
++};
++
++module_platform_driver(locomo_i2c_driver);
++
++MODULE_DESCRIPTION("LoCoMo i2c bus driver");
++MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:locomo-i2c");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0012-ARM-drop-old-LoCoMo-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0012-ARM-drop-old-LoCoMo-driver.patch
new file mode 100644
index 0000000..dd2cade
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0012-ARM-drop-old-LoCoMo-driver.patch
@@ -0,0 +1,1194 @@
+From 976687fce709a6b9e651a9eec3d930a69bd9b609 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 04:26:01 +0400
+Subject: [PATCH 12/20] ARM: drop old LoCoMo driver
+
+As there are no in-kernel users of old locomo driver, drop all
+alltogether. Remove Kconfig entry, driver and header.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ arch/arm/common/Kconfig | 3 -
+ arch/arm/common/Makefile | 1 -
+ arch/arm/common/locomo.c | 914 ---------------------------------
+ arch/arm/include/asm/hardware/locomo.h | 221 --------
+ 4 files changed, 1139 deletions(-)
+ delete mode 100644 arch/arm/common/locomo.c
+ delete mode 100644 arch/arm/include/asm/hardware/locomo.h
+
+diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
+index c3a4e9c..76c9970 100644
+--- a/arch/arm/common/Kconfig
++++ b/arch/arm/common/Kconfig
+@@ -9,9 +9,6 @@ config DMABOUNCE
+ bool
+ select ZONE_DMA
+
+-config SHARP_LOCOMO
+- bool
+-
+ config SHARP_PARAM
+ bool
+
+diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
+index 70b1eff..437c79a 100644
+--- a/arch/arm/common/Makefile
++++ b/arch/arm/common/Makefile
+@@ -7,7 +7,6 @@ obj-y += firmware.o
+ obj-$(CONFIG_ICST) += icst.o
+ obj-$(CONFIG_SA1111) += sa1111.o
+ obj-$(CONFIG_DMABOUNCE) += dmabounce.o
+-obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
+ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
+diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c
+deleted file mode 100644
+index b55c362..0000000
+--- a/arch/arm/common/locomo.c
++++ /dev/null
+@@ -1,914 +0,0 @@
+-/*
+- * 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>
+-#include <linux/platform_device.h>
+-#include <linux/slab.h>
+-#include <linux/spinlock.h>
+-#include <linux/io.h>
+-
+-#include <mach/hardware.h>
+-#include <asm/irq.h>
+-#include <asm/mach/irq.h>
+-
+-#include <asm/hardware/locomo.h>
+-
+-/* LoCoMo Interrupts */
+-#define IRQ_LOCOMO_KEY (0)
+-#define IRQ_LOCOMO_GPIO (1)
+-#define IRQ_LOCOMO_LT (2)
+-#define IRQ_LOCOMO_SPI (3)
+-
+-/* 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;
+- int irq_base;
+- spinlock_t lock;
+- void __iomem *base;
+-#ifdef CONFIG_PM
+- void *saved_state;
+-#endif
+-};
+-
+-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,
+- .irq = { IRQ_LOCOMO_KEY },
+- .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,
+- },
+- {
+- .devid = LOCOMO_DEVID_SPI,
+- .irq = {},
+- .name = "locomo-spi",
+- .offset = LOCOMO_SPI,
+- .length = 0x30,
+- },
+-};
+-
+-static void locomo_handler(unsigned int irq, struct irq_desc *desc)
+-{
+- struct locomo *lchip = irq_get_chip_data(irq);
+- int req, i;
+-
+- /* Acknowledge the parent IRQ */
+- desc->irq_data.chip->irq_ack(&desc->irq_data);
+-
+- /* check why this interrupt was generated */
+- req = locomo_readl(lchip->base + LOCOMO_ICR) & 0x0f00;
+-
+- if (req) {
+- /* generate the next interrupt(s) */
+- irq = lchip->irq_base;
+- for (i = 0; i <= 3; i++, irq++) {
+- if (req & (0x0100 << i)) {
+- generic_handle_irq(irq);
+- }
+-
+- }
+- }
+-}
+-
+-static void locomo_ack_irq(struct irq_data *d)
+-{
+-}
+-
+-static void locomo_mask_irq(struct irq_data *d)
+-{
+- struct locomo *lchip = irq_data_get_irq_chip_data(d);
+- unsigned int r;
+- r = locomo_readl(lchip->base + LOCOMO_ICR);
+- r &= ~(0x0010 << (d->irq - lchip->irq_base));
+- locomo_writel(r, lchip->base + LOCOMO_ICR);
+-}
+-
+-static void locomo_unmask_irq(struct irq_data *d)
+-{
+- struct locomo *lchip = irq_data_get_irq_chip_data(d);
+- unsigned int r;
+- r = locomo_readl(lchip->base + LOCOMO_ICR);
+- r |= (0x0010 << (d->irq - lchip->irq_base));
+- locomo_writel(r, lchip->base + LOCOMO_ICR);
+-}
+-
+-static struct irq_chip locomo_chip = {
+- .name = "LOCOMO",
+- .irq_ack = locomo_ack_irq,
+- .irq_mask = locomo_mask_irq,
+- .irq_unmask = locomo_unmask_irq,
+-};
+-
+-static void locomo_setup_irq(struct locomo *lchip)
+-{
+- int irq = lchip->irq_base;
+-
+- /*
+- * Install handler for IRQ_LOCOMO_HW.
+- */
+- 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);
+-
+- /* Install handlers for IRQ_LOCOMO_* */
+- for ( ; irq <= lchip->irq_base + 3; irq++) {
+- irq_set_chip_and_handler(irq, &locomo_chip, handle_level_irq);
+- irq_set_chip_data(irq, lchip);
+- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+- }
+-}
+-
+-
+-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;
+-
+- dev = kzalloc(sizeof(struct locomo_dev), GFP_KERNEL);
+- 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;
+- }
+-
+- dev_set_name(&dev->dev, "%s", info->name);
+- 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;
+-
+- dev->irq[0] = (lchip->irq_base == NO_IRQ) ?
+- NO_IRQ : lchip->irq_base + info->irq[0];
+-
+- ret = device_register(&dev->dev);
+- if (ret) {
+- out:
+- kfree(dev);
+- }
+- return ret;
+-}
+-
+-#ifdef CONFIG_PM
+-
+-struct locomo_save_data {
+- u16 LCM_GPO;
+- u16 LCM_SPICT;
+- u16 LCM_GPE;
+- u16 LCM_ASD;
+- u16 LCM_SPIMD;
+-};
+-
+-static int locomo_suspend(struct platform_device *dev, pm_message_t state)
+-{
+- struct locomo *lchip = platform_get_drvdata(dev);
+- struct locomo_save_data *save;
+- unsigned long flags;
+-
+- save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL);
+- if (!save)
+- return -ENOMEM;
+-
+- lchip->saved_state = save;
+-
+- spin_lock_irqsave(&lchip->lock, flags);
+-
+- save->LCM_GPO = locomo_readl(lchip->base + LOCOMO_GPO); /* GPIO */
+- locomo_writel(0x00, lchip->base + LOCOMO_GPO);
+- save->LCM_SPICT = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPICT); /* SPI */
+- locomo_writel(0x40, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
+- 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);
+- save->LCM_SPIMD = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPIMD); /* SPI */
+- locomo_writel(0x3C14, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
+-
+- locomo_writel(0x00, lchip->base + LOCOMO_PAIF);
+- locomo_writel(0x00, lchip->base + LOCOMO_DAC);
+- locomo_writel(0x00, lchip->base + LOCOMO_BACKLIGHT + LOCOMO_TC);
+-
+- if ((locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT0) & 0x88) && (locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT1) & 0x88))
+- 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;
+-}
+-
+-static int locomo_resume(struct platform_device *dev)
+-{
+- struct locomo *lchip = platform_get_drvdata(dev);
+- struct locomo_save_data *save;
+- unsigned long r;
+- unsigned long flags;
+-
+- save = lchip->saved_state;
+- if (!save)
+- return 0;
+-
+- spin_lock_irqsave(&lchip->lock, flags);
+-
+- locomo_writel(save->LCM_GPO, lchip->base + LOCOMO_GPO);
+- locomo_writel(save->LCM_SPICT, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
+- locomo_writel(save->LCM_GPE, lchip->base + LOCOMO_GPE);
+- locomo_writel(save->LCM_ASD, lchip->base + LOCOMO_ASD);
+- locomo_writel(save->LCM_SPIMD, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
+-
+- 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);
+-
+- lchip->saved_state = NULL;
+- kfree(save);
+-
+- return 0;
+-}
+-#endif
+-
+-
+-/**
+- * 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)
+-{
+- struct locomo_platform_data *pdata = me->platform_data;
+- struct locomo *lchip;
+- unsigned long r;
+- int i, ret = -ENODEV;
+-
+- lchip = kzalloc(sizeof(struct locomo), GFP_KERNEL);
+- 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;
+- lchip->irq_base = (pdata) ? pdata->irq_base : NO_IRQ;
+-
+- /*
+- * 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);
+- locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+- , lchip->base + LOCOMO_GPE);
+- locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+- , lchip->base + LOCOMO_GPD);
+- locomo_writel(0, lchip->base + LOCOMO_GIE);
+-
+- /* Frontlight */
+- locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
+- locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
+-
+- /* Longtime timer */
+- locomo_writel(0, lchip->base + LOCOMO_LTINT);
+- /* SPI */
+- locomo_writel(0, lchip->base + LOCOMO_SPI + LOCOMO_SPIIE);
+-
+- 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.
+- */
+- if (lchip->irq != NO_IRQ && lchip->irq_base != NO_IRQ)
+- 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;
+-}
+-
+-static int locomo_remove_child(struct device *dev, void *data)
+-{
+- device_unregister(dev);
+- return 0;
+-}
+-
+-static void __locomo_remove(struct locomo *lchip)
+-{
+- device_for_each_child(lchip->dev, NULL, locomo_remove_child);
+-
+- if (lchip->irq != NO_IRQ) {
+- irq_set_chained_handler(lchip->irq, NULL);
+- irq_set_handler_data(lchip->irq, NULL);
+- }
+-
+- iounmap(lchip->base);
+- kfree(lchip);
+-}
+-
+-static int locomo_probe(struct platform_device *dev)
+-{
+- struct resource *mem;
+- int irq;
+-
+- mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+- if (!mem)
+- return -EINVAL;
+- irq = platform_get_irq(dev, 0);
+- if (irq < 0)
+- return -ENXIO;
+-
+- return __locomo_probe(&dev->dev, mem, irq);
+-}
+-
+-static int locomo_remove(struct platform_device *dev)
+-{
+- struct locomo *lchip = platform_get_drvdata(dev);
+-
+- if (lchip) {
+- __locomo_remove(lchip);
+- platform_set_drvdata(dev, NULL);
+- }
+-
+- 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.
+- */
+-static struct platform_driver locomo_device_driver = {
+- .probe = locomo_probe,
+- .remove = locomo_remove,
+-#ifdef CONFIG_PM
+- .suspend = locomo_suspend,
+- .resume = locomo_resume,
+-#endif
+- .driver = {
+- .name = "locomo",
+- },
+-};
+-
+-/*
+- * 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);
+-}
+-
+-void locomo_gpio_set_dir(struct device *dev, unsigned int bits, unsigned int dir)
+-{
+- struct locomo *lchip = dev_get_drvdata(dev);
+- unsigned long flags;
+- unsigned int r;
+-
+- if (!lchip)
+- return;
+-
+- spin_lock_irqsave(&lchip->lock, flags);
+-
+- r = locomo_readl(lchip->base + LOCOMO_GPD);
+- if (dir)
+- r |= bits;
+- else
+- r &= ~bits;
+- 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);
+-}
+-EXPORT_SYMBOL(locomo_gpio_set_dir);
+-
+-int locomo_gpio_read_level(struct device *dev, unsigned int bits)
+-{
+- struct locomo *lchip = dev_get_drvdata(dev);
+- unsigned long flags;
+- unsigned int ret;
+-
+- if (!lchip)
+- return -ENODEV;
+-
+- spin_lock_irqsave(&lchip->lock, flags);
+- ret = locomo_readl(lchip->base + LOCOMO_GPL);
+- spin_unlock_irqrestore(&lchip->lock, flags);
+-
+- ret &= bits;
+- return ret;
+-}
+-EXPORT_SYMBOL(locomo_gpio_read_level);
+-
+-int locomo_gpio_read_output(struct device *dev, unsigned int bits)
+-{
+- struct locomo *lchip = dev_get_drvdata(dev);
+- unsigned long flags;
+- unsigned int ret;
+-
+- if (!lchip)
+- return -ENODEV;
+-
+- spin_lock_irqsave(&lchip->lock, flags);
+- ret = locomo_readl(lchip->base + LOCOMO_GPO);
+- spin_unlock_irqrestore(&lchip->lock, flags);
+-
+- ret &= bits;
+- return ret;
+-}
+-EXPORT_SYMBOL(locomo_gpio_read_output);
+-
+-void locomo_gpio_write(struct device *dev, unsigned int bits, unsigned int set)
+-{
+- struct locomo *lchip = dev_get_drvdata(dev);
+- unsigned long flags;
+- unsigned int r;
+-
+- if (!lchip)
+- return;
+-
+- 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);
+-}
+-EXPORT_SYMBOL(locomo_gpio_write);
+-
+-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");
+- goto out;
+- }
+-
+- /* 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");
+- goto out;
+- }
+-
+- /* 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");
+- }
+-
+-out:
+- /* 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);
+-}
+-EXPORT_SYMBOL(locomo_m62332_senddata);
+-
+-/*
+- * 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)
+- locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 1);
+- else
+- locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
+-
+- 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);
+-}
+-EXPORT_SYMBOL(locomo_frontlight_set);
+-
+-/*
+- * 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,
+- .probe = locomo_bus_probe,
+- .remove = locomo_bus_remove,
+- .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);
+-}
+-EXPORT_SYMBOL(locomo_driver_register);
+-
+-void locomo_driver_unregister(struct locomo_driver *driver)
+-{
+- driver_unregister(&driver->drv);
+-}
+-EXPORT_SYMBOL(locomo_driver_unregister);
+-
+-static int __init locomo_init(void)
+-{
+- int ret = bus_register(&locomo_bus_type);
+- if (ret == 0)
+- platform_driver_register(&locomo_device_driver);
+- return ret;
+-}
+-
+-static void __exit locomo_exit(void)
+-{
+- platform_driver_unregister(&locomo_device_driver);
+- 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>");
+diff --git a/arch/arm/include/asm/hardware/locomo.h b/arch/arm/include/asm/hardware/locomo.h
+deleted file mode 100644
+index 74e51d6b..0000000
+--- a/arch/arm/include/asm/hardware/locomo.h
++++ /dev/null
+@@ -1,221 +0,0 @@
+-/*
+- * arch/arm/include/asm/hardware/locomo.h
+- *
+- * This file contains the definitions for the LoCoMo G/A Chip
+- *
+- * (C) Copyright 2004 John Lenz
+- *
+- * May be copied or modified under the terms of the GNU General Public
+- * License. See linux/COPYING for more information.
+- *
+- * Based on sa1111.h
+- */
+-#ifndef _ASM_ARCH_LOCOMO
+-#define _ASM_ARCH_LOCOMO
+-
+-#define locomo_writel(val,addr) ({ *(volatile u16 *)(addr) = (val); })
+-#define locomo_readl(addr) (*(volatile u16 *)(addr))
+-
+-/* LOCOMO version */
+-#define LOCOMO_VER 0x00
+-
+-/* Pin status */
+-#define LOCOMO_ST 0x04
+-
+-/* Pin status */
+-#define LOCOMO_C32K 0x08
+-
+-/* Interrupt controller */
+-#define LOCOMO_ICR 0x0C
+-
+-/* MCS decoder for boot selecting */
+-#define LOCOMO_MCSX0 0x10
+-#define LOCOMO_MCSX1 0x14
+-#define LOCOMO_MCSX2 0x18
+-#define LOCOMO_MCSX3 0x1c
+-
+-/* Touch panel controller */
+-#define LOCOMO_ASD 0x20 /* AD start delay */
+-#define LOCOMO_HSD 0x28 /* HSYS delay */
+-#define LOCOMO_HSC 0x2c /* HSYS period */
+-#define LOCOMO_TADC 0x30 /* tablet ADC clock */
+-
+-
+-/* Long time timer */
+-#define LOCOMO_LTC 0xd8 /* LTC interrupt setting */
+-#define LOCOMO_LTINT 0xdc /* LTC interrupt */
+-
+-/* DAC control signal for LCD (COMADJ ) */
+-#define LOCOMO_DAC 0xe0
+-/* DAC control */
+-#define LOCOMO_DAC_SCLOEB 0x08 /* SCL pin output data */
+-#define LOCOMO_DAC_TEST 0x04 /* Test bit */
+-#define LOCOMO_DAC_SDA 0x02 /* SDA pin level (read-only) */
+-#define LOCOMO_DAC_SDAOEB 0x01 /* SDA pin output data */
+-
+-/* SPI interface */
+-#define LOCOMO_SPI 0x60
+-#define LOCOMO_SPIMD 0x00 /* SPI mode setting */
+-#define LOCOMO_SPICT 0x04 /* SPI mode control */
+-#define LOCOMO_SPIST 0x08 /* SPI status */
+-#define LOCOMO_SPI_TEND (1 << 3) /* Transfer end bit */
+-#define LOCOMO_SPI_REND (1 << 2) /* Receive end bit */
+-#define LOCOMO_SPI_RFW (1 << 1) /* write buffer bit */
+-#define LOCOMO_SPI_RFR (1) /* read buffer bit */
+-
+-#define LOCOMO_SPIIS 0x10 /* SPI interrupt status */
+-#define LOCOMO_SPIWE 0x14 /* SPI interrupt status write enable */
+-#define LOCOMO_SPIIE 0x18 /* SPI interrupt enable */
+-#define LOCOMO_SPIIR 0x1c /* SPI interrupt request */
+-#define LOCOMO_SPITD 0x20 /* SPI transfer data write */
+-#define LOCOMO_SPIRD 0x24 /* SPI receive data read */
+-#define LOCOMO_SPITS 0x28 /* SPI transfer data shift */
+-#define LOCOMO_SPIRS 0x2C /* SPI receive data shift */
+-
+-/* GPIO */
+-#define LOCOMO_GPD 0x90 /* GPIO direction */
+-#define LOCOMO_GPE 0x94 /* GPIO input enable */
+-#define LOCOMO_GPL 0x98 /* GPIO level */
+-#define LOCOMO_GPO 0x9c /* GPIO out data setting */
+-#define LOCOMO_GRIE 0xa0 /* GPIO rise detection */
+-#define LOCOMO_GFIE 0xa4 /* GPIO fall detection */
+-#define LOCOMO_GIS 0xa8 /* GPIO edge detection status */
+-#define LOCOMO_GWE 0xac /* GPIO status write enable */
+-#define LOCOMO_GIE 0xb0 /* GPIO interrupt enable */
+-#define LOCOMO_GIR 0xb4 /* GPIO interrupt request */
+-#define LOCOMO_GPIO(Nb) (0x01 << (Nb))
+-#define LOCOMO_GPIO_RTS LOCOMO_GPIO(0)
+-#define LOCOMO_GPIO_CTS LOCOMO_GPIO(1)
+-#define LOCOMO_GPIO_DSR LOCOMO_GPIO(2)
+-#define LOCOMO_GPIO_DTR LOCOMO_GPIO(3)
+-#define LOCOMO_GPIO_LCD_VSHA_ON LOCOMO_GPIO(4)
+-#define LOCOMO_GPIO_LCD_VSHD_ON LOCOMO_GPIO(5)
+-#define LOCOMO_GPIO_LCD_VEE_ON LOCOMO_GPIO(6)
+-#define LOCOMO_GPIO_LCD_MOD LOCOMO_GPIO(7)
+-#define LOCOMO_GPIO_DAC_ON LOCOMO_GPIO(8)
+-#define LOCOMO_GPIO_FL_VR LOCOMO_GPIO(9)
+-#define LOCOMO_GPIO_DAC_SDATA LOCOMO_GPIO(10)
+-#define LOCOMO_GPIO_DAC_SCK LOCOMO_GPIO(11)
+-#define LOCOMO_GPIO_DAC_SLOAD LOCOMO_GPIO(12)
+-#define LOCOMO_GPIO_CARD_DETECT LOCOMO_GPIO(13)
+-#define LOCOMO_GPIO_WRITE_PROT LOCOMO_GPIO(14)
+-#define LOCOMO_GPIO_CARD_POWER LOCOMO_GPIO(15)
+-
+-/* Start the definitions of the devices. Each device has an initial
+- * base address and a series of offsets from that base address. */
+-
+-/* Keyboard controller */
+-#define LOCOMO_KEYBOARD 0x40
+-#define LOCOMO_KIB 0x00 /* KIB level */
+-#define LOCOMO_KSC 0x04 /* KSTRB control */
+-#define LOCOMO_KCMD 0x08 /* KSTRB command */
+-#define LOCOMO_KIC 0x0c /* Key interrupt */
+-
+-/* Front light adjustment controller */
+-#define LOCOMO_FRONTLIGHT 0xc8
+-#define LOCOMO_ALS 0x00 /* Adjust light cycle */
+-#define LOCOMO_ALD 0x04 /* Adjust light duty */
+-
+-#define LOCOMO_ALC_EN 0x8000
+-
+-/* Backlight controller: TFT signal */
+-#define LOCOMO_BACKLIGHT 0x38
+-#define LOCOMO_TC 0x00 /* TFT control signal */
+-#define LOCOMO_CPSD 0x04 /* CPS delay */
+-
+-/* Audio controller */
+-#define LOCOMO_AUDIO 0x54
+-#define LOCOMO_ACC 0x00 /* Audio clock */
+-#define LOCOMO_PAIF 0xD0 /* PCM audio interface */
+-/* Audio clock */
+-#define LOCOMO_ACC_XON 0x80
+-#define LOCOMO_ACC_XEN 0x40
+-#define LOCOMO_ACC_XSEL0 0x00
+-#define LOCOMO_ACC_XSEL1 0x20
+-#define LOCOMO_ACC_MCLKEN 0x10
+-#define LOCOMO_ACC_64FSEN 0x08
+-#define LOCOMO_ACC_CLKSEL000 0x00 /* mclk 2 */
+-#define LOCOMO_ACC_CLKSEL001 0x01 /* mclk 3 */
+-#define LOCOMO_ACC_CLKSEL010 0x02 /* mclk 4 */
+-#define LOCOMO_ACC_CLKSEL011 0x03 /* mclk 6 */
+-#define LOCOMO_ACC_CLKSEL100 0x04 /* mclk 8 */
+-#define LOCOMO_ACC_CLKSEL101 0x05 /* mclk 12 */
+-/* PCM audio interface */
+-#define LOCOMO_PAIF_SCINV 0x20
+-#define LOCOMO_PAIF_SCEN 0x10
+-#define LOCOMO_PAIF_LRCRST 0x08
+-#define LOCOMO_PAIF_LRCEVE 0x04
+-#define LOCOMO_PAIF_LRCINV 0x02
+-#define LOCOMO_PAIF_LRCEN 0x01
+-
+-/* LED controller */
+-#define LOCOMO_LED 0xe8
+-#define LOCOMO_LPT0 0x00
+-#define LOCOMO_LPT1 0x04
+-/* LED control */
+-#define LOCOMO_LPT_TOFH 0x80
+-#define LOCOMO_LPT_TOFL 0x08
+-#define LOCOMO_LPT_TOH(TOH) ((TOH & 0x7) << 4)
+-#define LOCOMO_LPT_TOL(TOL) ((TOL & 0x7))
+-
+-extern struct bus_type locomo_bus_type;
+-
+-#define LOCOMO_DEVID_KEYBOARD 0
+-#define LOCOMO_DEVID_FRONTLIGHT 1
+-#define LOCOMO_DEVID_BACKLIGHT 2
+-#define LOCOMO_DEVID_AUDIO 3
+-#define LOCOMO_DEVID_LED 4
+-#define LOCOMO_DEVID_UART 5
+-#define LOCOMO_DEVID_SPI 6
+-
+-struct locomo_dev {
+- struct device dev;
+- unsigned int devid;
+- unsigned int irq[1];
+-
+- void *mapbase;
+- unsigned long length;
+-
+- u64 dma_mask;
+-};
+-
+-#define LOCOMO_DEV(_d) container_of((_d), struct locomo_dev, dev)
+-
+-#define locomo_get_drvdata(d) dev_get_drvdata(&(d)->dev)
+-#define locomo_set_drvdata(d,p) dev_set_drvdata(&(d)->dev, p)
+-
+-struct locomo_driver {
+- struct device_driver drv;
+- unsigned int devid;
+- int (*probe)(struct locomo_dev *);
+- int (*remove)(struct locomo_dev *);
+- int (*suspend)(struct locomo_dev *, pm_message_t);
+- int (*resume)(struct locomo_dev *);
+-};
+-
+-#define LOCOMO_DRV(_d) container_of((_d), struct locomo_driver, drv)
+-
+-#define LOCOMO_DRIVER_NAME(_ldev) ((_ldev)->dev.driver->name)
+-
+-void locomo_lcd_power(struct locomo_dev *, int, unsigned int);
+-
+-int locomo_driver_register(struct locomo_driver *);
+-void locomo_driver_unregister(struct locomo_driver *);
+-
+-/* GPIO control functions */
+-void locomo_gpio_set_dir(struct device *dev, unsigned int bits, unsigned int dir);
+-int locomo_gpio_read_level(struct device *dev, unsigned int bits);
+-int locomo_gpio_read_output(struct device *dev, unsigned int bits);
+-void locomo_gpio_write(struct device *dev, unsigned int bits, unsigned int set);
+-
+-/* M62332 control function */
+-void locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel);
+-
+-/* Frontlight control */
+-void locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf);
+-
+-struct locomo_platform_data {
+- int irq_base; /* IRQ base for cascaded on-chip IRQs */
+-};
+-
+-#endif
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0013-video-backlight-drop-old-locomo-bl-lcd-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0013-video-backlight-drop-old-locomo-bl-lcd-driver.patch
new file mode 100644
index 0000000..1713af7
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0013-video-backlight-drop-old-locomo-bl-lcd-driver.patch
@@ -0,0 +1,278 @@
+From 19c650358a308711b8f7e6e46acf778596f8c45a Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Thu, 5 Dec 2013 00:07:17 +0400
+Subject: [PATCH 13/20] video: backlight: drop old locomo bl/lcd driver
+
+Old locomolcd driver is now completely obsolete by new locomo_bl and
+locomo_lcd drivers, so let's drop it.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/video/backlight/locomolcd.c | 255 ------------------------------------
+ 1 file changed, 255 deletions(-)
+ delete mode 100644 drivers/video/backlight/locomolcd.c
+
+diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
+deleted file mode 100644
+index 6c3ec42..0000000
+--- a/drivers/video/backlight/locomolcd.c
++++ /dev/null
+@@ -1,255 +0,0 @@
+-/*
+- * Backlight control code for Sharp Zaurus SL-5500
+- *
+- * Copyright 2005 John Lenz <lenz@cs.wisc.edu>
+- * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
+- * GPL v2
+- *
+- * This driver assumes single CPU. That's okay, because collie is
+- * slightly old hardware, and no one is going to retrofit second CPU to
+- * old PDA.
+- */
+-
+-/* LCD power functions */
+-#include <linux/module.h>
+-#include <linux/init.h>
+-#include <linux/delay.h>
+-#include <linux/device.h>
+-#include <linux/interrupt.h>
+-#include <linux/fb.h>
+-#include <linux/backlight.h>
+-
+-#include <asm/hardware/locomo.h>
+-#include <asm/irq.h>
+-#include <asm/mach/sharpsl_param.h>
+-#include <asm/mach-types.h>
+-
+-#include "../../../arch/arm/mach-sa1100/generic.h"
+-
+-static struct backlight_device *locomolcd_bl_device;
+-static struct locomo_dev *locomolcd_dev;
+-static unsigned long locomolcd_flags;
+-#define LOCOMOLCD_SUSPENDED 0x01
+-
+-static void locomolcd_on(int comadj)
+-{
+- locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1);
+- mdelay(2);
+-
+- locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1);
+- mdelay(2);
+-
+- locomo_m62332_senddata(locomolcd_dev, comadj, 0);
+- mdelay(5);
+-
+- locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1);
+- mdelay(10);
+-
+- /* TFTCRST | CPSOUT=0 | CPSEN */
+- locomo_writel(0x01, locomolcd_dev->mapbase + LOCOMO_TC);
+-
+- /* Set CPSD */
+- locomo_writel(6, locomolcd_dev->mapbase + LOCOMO_CPSD);
+-
+- /* TFTCRST | CPSOUT=0 | CPSEN */
+- locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC);
+- mdelay(10);
+-
+- locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1);
+-}
+-
+-static void locomolcd_off(int comadj)
+-{
+- /* TFTCRST=1 | CPSOUT=1 | CPSEN = 0 */
+- locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC);
+- mdelay(1);
+-
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+- mdelay(110);
+-
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
+- mdelay(700);
+-
+- /* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
+- locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+- locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+-}
+-
+-void locomolcd_power(int on)
+-{
+- int comadj = sharpsl_param.comadj;
+- unsigned long flags;
+-
+- local_irq_save(flags);
+-
+- if (!locomolcd_dev) {
+- local_irq_restore(flags);
+- return;
+- }
+-
+- /* read comadj */
+- if (comadj == -1 && machine_is_collie())
+- comadj = 128;
+- if (comadj == -1 && machine_is_poodle())
+- comadj = 118;
+-
+- if (on)
+- locomolcd_on(comadj);
+- else
+- locomolcd_off(comadj);
+-
+- local_irq_restore(flags);
+-}
+-EXPORT_SYMBOL(locomolcd_power);
+-
+-static int current_intensity;
+-
+-static int locomolcd_set_intensity(struct backlight_device *bd)
+-{
+- int intensity = bd->props.brightness;
+-
+- if (bd->props.power != FB_BLANK_UNBLANK)
+- intensity = 0;
+- if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+- intensity = 0;
+- if (locomolcd_flags & LOCOMOLCD_SUSPENDED)
+- intensity = 0;
+-
+- switch (intensity) {
+- /*
+- * AC and non-AC are handled differently,
+- * but produce same results in sharp code?
+- */
+- case 0:
+- locomo_frontlight_set(locomolcd_dev, 0, 0, 161);
+- break;
+- case 1:
+- locomo_frontlight_set(locomolcd_dev, 117, 0, 161);
+- break;
+- case 2:
+- locomo_frontlight_set(locomolcd_dev, 163, 0, 148);
+- break;
+- case 3:
+- locomo_frontlight_set(locomolcd_dev, 194, 0, 161);
+- break;
+- case 4:
+- locomo_frontlight_set(locomolcd_dev, 194, 1, 161);
+- break;
+- default:
+- return -ENODEV;
+- }
+- current_intensity = intensity;
+- return 0;
+-}
+-
+-static int locomolcd_get_intensity(struct backlight_device *bd)
+-{
+- return current_intensity;
+-}
+-
+-static const struct backlight_ops locomobl_data = {
+- .get_brightness = locomolcd_get_intensity,
+- .update_status = locomolcd_set_intensity,
+-};
+-
+-#ifdef CONFIG_PM_SLEEP
+-static int locomolcd_suspend(struct device *dev)
+-{
+- locomolcd_flags |= LOCOMOLCD_SUSPENDED;
+- locomolcd_set_intensity(locomolcd_bl_device);
+- return 0;
+-}
+-
+-static int locomolcd_resume(struct device *dev)
+-{
+- locomolcd_flags &= ~LOCOMOLCD_SUSPENDED;
+- locomolcd_set_intensity(locomolcd_bl_device);
+- return 0;
+-}
+-#endif
+-
+-static SIMPLE_DEV_PM_OPS(locomolcd_pm_ops, locomolcd_suspend, locomolcd_resume);
+-
+-static int locomolcd_probe(struct locomo_dev *ldev)
+-{
+- struct backlight_properties props;
+- unsigned long flags;
+-
+- local_irq_save(flags);
+- locomolcd_dev = ldev;
+-
+- locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
+-
+- /*
+- * the poodle_lcd_power function is called for the first time
+- * from fs_initcall, which is before locomo is activated.
+- * We need to recall poodle_lcd_power here
+- */
+- if (machine_is_poodle())
+- locomolcd_power(1);
+-
+- local_irq_restore(flags);
+-
+- memset(&props, 0, sizeof(struct backlight_properties));
+- props.type = BACKLIGHT_RAW;
+- props.max_brightness = 4;
+- locomolcd_bl_device = backlight_device_register("locomo-bl",
+- &ldev->dev, NULL,
+- &locomobl_data, &props);
+-
+- if (IS_ERR(locomolcd_bl_device))
+- return PTR_ERR(locomolcd_bl_device);
+-
+- /* Set up frontlight so that screen is readable */
+- locomolcd_bl_device->props.brightness = 2;
+- locomolcd_set_intensity(locomolcd_bl_device);
+-
+- return 0;
+-}
+-
+-static int locomolcd_remove(struct locomo_dev *dev)
+-{
+- unsigned long flags;
+-
+- locomolcd_bl_device->props.brightness = 0;
+- locomolcd_bl_device->props.power = 0;
+- locomolcd_set_intensity(locomolcd_bl_device);
+-
+- backlight_device_unregister(locomolcd_bl_device);
+- local_irq_save(flags);
+- locomolcd_dev = NULL;
+- local_irq_restore(flags);
+- return 0;
+-}
+-
+-static struct locomo_driver poodle_lcd_driver = {
+- .drv = {
+- .name = "locomo-backlight",
+- .pm = &locomolcd_pm_ops,
+- },
+- .devid = LOCOMO_DEVID_BACKLIGHT,
+- .probe = locomolcd_probe,
+- .remove = locomolcd_remove,
+-};
+-
+-static int __init locomolcd_init(void)
+-{
+- return locomo_driver_register(&poodle_lcd_driver);
+-}
+-
+-static void __exit locomolcd_exit(void)
+-{
+- locomo_driver_unregister(&poodle_lcd_driver);
+-}
+-
+-module_init(locomolcd_init);
+-module_exit(locomolcd_exit);
+-
+-MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@ucw.cz>");
+-MODULE_DESCRIPTION("Collie LCD driver");
+-MODULE_LICENSE("GPL");
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0014-ARM-sa1100-make-collie-use-new-locomo-drivers.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0014-ARM-sa1100-make-collie-use-new-locomo-drivers.patch
new file mode 100644
index 0000000..31a72d5
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0014-ARM-sa1100-make-collie-use-new-locomo-drivers.patch
@@ -0,0 +1,408 @@
+From 3b7a269471c659761c7e2dfef772404270612b4e Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 03:12:39 +0400
+Subject: [PATCH 14/20] ARM: sa1100: make collie use new locomo drivers
+
+Switch collie to new mfd-based locomo driver. Update platform data and
+add necessary spi, i2c and regulator devices.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-sa1100/Kconfig | 1 -
+ arch/arm/mach-sa1100/collie.c | 213 ++++++++++++++++++++++-------
+ arch/arm/mach-sa1100/include/mach/collie.h | 25 +++-
+ 3 files changed, 187 insertions(+), 52 deletions(-)
+
+diff --git a/arch/arm/mach-sa1100/Kconfig b/arch/arm/mach-sa1100/Kconfig
+index c6f6ed1..37af126 100644
+--- a/arch/arm/mach-sa1100/Kconfig
++++ b/arch/arm/mach-sa1100/Kconfig
+@@ -48,7 +48,6 @@ endchoice
+ config SA1100_COLLIE
+ bool "Sharp Zaurus SL5500"
+ # FIXME: select ARM_SA11x0_CPUFREQ
+- select SHARP_LOCOMO
+ select SHARP_PARAM
+ select SHARP_SCOOP
+ help
+diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c
+index 3cc2b71..5bdc4bd 100644
+--- a/arch/arm/mach-sa1100/collie.c
++++ b/arch/arm/mach-sa1100/collie.c
+@@ -24,13 +24,21 @@
+ #include <linux/platform_data/sa11x0-serial.h>
+ #include <linux/platform_device.h>
+ #include <linux/mfd/ucb1x00.h>
++#include <linux/mfd/locomo.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/timer.h>
+ #include <linux/gpio_keys.h>
+ #include <linux/input.h>
++#include <linux/i2c.h>
+ #include <linux/gpio.h>
++#include <linux/gpio/machine.h>
+ #include <linux/power/gpio-charger.h>
++#include <linux/iio/machine.h>
++#include <linux/mmc/host.h>
++#include <linux/regulator/fixed.h>
++#include <linux/regulator/machine.h>
++#include <linux/spi/mmc_spi.h>
+
+ #include <video/sa1100fb.h>
+
+@@ -47,7 +55,6 @@
+
+ #include <asm/hardware/scoop.h>
+ #include <asm/mach/sharpsl_param.h>
+-#include <asm/hardware/locomo.h>
+ #include <linux/platform_data/mfd-mcp-sa11x0.h>
+ #include <mach/irqs.h>
+
+@@ -151,36 +158,54 @@ static struct platform_device collie_power_device = {
+ .dev.platform_data = &collie_power_data,
+ };
+
+-#ifdef CONFIG_SHARP_LOCOMO
+ /*
+ * low-level UART features.
+ */
+-struct platform_device collie_locomo_device;
++static struct gpio collie_uart_gpio[] = {
++ { COLLIE_GPIO_CTS, GPIOF_IN, "CTS" },
++ { COLLIE_GPIO_RTS, GPIOF_OUT_INIT_LOW, "RTS" },
++ { COLLIE_GPIO_DTR, GPIOF_OUT_INIT_LOW, "DTR" },
++ { COLLIE_GPIO_DSR, GPIOF_IN, "DSR" },
++};
++
++static bool collie_uart_gpio_ok;
+
+ static void collie_uart_set_mctrl(struct uart_port *port, u_int mctrl)
+ {
+- if (mctrl & TIOCM_RTS)
+- locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 0);
+- else
+- locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 1);
+-
+- if (mctrl & TIOCM_DTR)
+- locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 0);
+- else
+- locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 1);
++ if (!collie_uart_gpio_ok) {
++ int rc = gpio_request_array(collie_uart_gpio,
++ ARRAY_SIZE(collie_uart_gpio));
++ if (rc)
++ pr_err("collie_uart_set_mctrl: gpio request %d\n", rc);
++ else
++ collie_uart_gpio_ok = true;
++ }
++
++ if (collie_uart_gpio_ok) {
++ gpio_set_value(COLLIE_GPIO_RTS, !(mctrl & TIOCM_RTS));
++ gpio_set_value(COLLIE_GPIO_DTR, !(mctrl & TIOCM_DTR));
++ }
+ }
+
+ static u_int collie_uart_get_mctrl(struct uart_port *port)
+ {
+ int ret = TIOCM_CD;
+- unsigned int r;
+
+- r = locomo_gpio_read_output(&collie_locomo_device.dev, LOCOMO_GPIO_CTS & LOCOMO_GPIO_DSR);
+- if (r == -ENODEV)
++ if (!collie_uart_gpio_ok) {
++ int rc = gpio_request_array(collie_uart_gpio,
++ ARRAY_SIZE(collie_uart_gpio));
++ if (rc)
++ pr_err("collie_uart_get_mctrl: gpio request %d\n", rc);
++ else
++ collie_uart_gpio_ok = true;
++ }
++
++ if (!collie_uart_gpio_ok)
+ return ret;
+- if (r & LOCOMO_GPIO_CTS)
++
++ if (gpio_get_value(COLLIE_GPIO_CTS))
+ ret |= TIOCM_CTS;
+- if (r & LOCOMO_GPIO_DSR)
++ if (gpio_get_value(COLLIE_GPIO_DSR))
+ ret |= TIOCM_DSR;
+
+ return ret;
+@@ -191,33 +216,35 @@ static struct sa1100_port_fns collie_port_fns __initdata = {
+ .get_mctrl = collie_uart_get_mctrl,
+ };
+
+-static int collie_uart_probe(struct locomo_dev *dev)
+-{
+- return 0;
+-}
+-
+-static int collie_uart_remove(struct locomo_dev *dev)
+-{
+- return 0;
+-}
++static struct regulator_consumer_supply collie_amp_on_consumer_supplies[] = {
++ REGULATOR_SUPPLY("VCC", "1-004e"),
++};
+
+-static struct locomo_driver collie_uart_driver = {
+- .drv = {
+- .name = "collie_uart",
++static struct regulator_init_data collie_amp_on_init_data = {
++ .constraints = {
++ .name = "AMP_ON",
++ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+- .devid = LOCOMO_DEVID_UART,
+- .probe = collie_uart_probe,
+- .remove = collie_uart_remove,
++ .consumer_supplies = collie_amp_on_consumer_supplies,
++ .num_consumer_supplies = ARRAY_SIZE(collie_amp_on_consumer_supplies),
+ };
+
+-static int __init collie_uart_init(void)
+-{
+- return locomo_driver_register(&collie_uart_driver);
+-}
+-device_initcall(collie_uart_init);
+-
+-#endif
++static struct fixed_voltage_config collie_amp_on_data = {
++ .supply_name = "amp_on",
++ .microvolts = 3300000,
++ .gpio = COLLIE_GPIO_AMP2_ON,
++ .startup_delay = 5,
++ .enable_high = 1,
++ .init_data = &collie_amp_on_init_data,
++};
+
++static struct platform_device collie_amp_on_device = {
++ .name = "reg-fixed-voltage",
++ .id = -1,
++ .dev = {
++ .platform_data = &collie_amp_on_data,
++ },
++};
+
+ static struct resource locomo_resources[] = {
+ [0] = DEFINE_RES_MEM(0x40000000, SZ_8K),
+@@ -225,14 +252,15 @@ static struct resource locomo_resources[] = {
+ };
+
+ static struct locomo_platform_data locomo_info = {
+- .irq_base = IRQ_BOARD_START,
++ .gpio_base = COLLIE_LOCOMO_GPIO_BASE,
++ .comadj = 128,
+ };
+
+-struct platform_device collie_locomo_device = {
++static struct platform_device collie_locomo_device = {
+ .name = "locomo",
+ .id = 0,
+ .dev = {
+- .platform_data = &locomo_info,
++ .platform_data = &locomo_info,
+ },
+ .num_resources = ARRAY_SIZE(locomo_resources),
+ .resource = locomo_resources,
+@@ -270,7 +298,55 @@ static struct platform_device collie_gpio_keys_device = {
+ },
+ };
+
++static int collie_mmc_init(struct device *dev,
++ irqreturn_t (*isr)(int, void*), void *mmc)
++{
++ int ret;
++
++ ret = gpio_request(COLLIE_GPIO_CARD_POWER, "MMC power");
++ if (!ret)
++ ret = gpio_direction_output(COLLIE_GPIO_CARD_POWER, 0);
++ if (ret)
++ gpio_free(COLLIE_GPIO_CARD_POWER);
++ return ret;
++}
++
++static void collie_mmc_exit(struct device *dev, void *mmc)
++{
++ gpio_free(COLLIE_GPIO_CARD_POWER);
++}
++
++static void collie_mmc_setpower(struct device *dev, unsigned int mask)
++{
++ gpio_set_value(COLLIE_GPIO_CARD_POWER, !!mask);
++}
++
++static struct mmc_spi_platform_data collie_mmc_data = {
++ .init = collie_mmc_init,
++ .exit = collie_mmc_exit,
++ .setpower = collie_mmc_setpower,
++ .detect_delay = 200,
++ .powerup_msecs = 200,
++ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
++ .flags = MMC_SPI_USE_CD_GPIO | MMC_SPI_USE_RO_GPIO,
++ .cd_gpio = COLLIE_GPIO_CARD_DETECT,
++ .ro_gpio = COLLIE_GPIO_CARD_RO,
++ .caps2 = MMC_CAP2_RO_ACTIVE_HIGH,
++};
++
++static struct spi_board_info collie_spi_board_info[] __initdata = {
++ {
++ .modalias = "mmc_spi",
++ .platform_data = &collie_mmc_data,
++ .max_speed_hz = 25000000,
++ .bus_num = 0,
++ .chip_select = 0,
++ .mode = SPI_MODE_0,
++ },
++};
++
+ static struct platform_device *devices[] __initdata = {
++ &collie_amp_on_device,
+ &collie_locomo_device,
+ &colliescoop_device,
+ &collie_power_device,
+@@ -347,10 +423,39 @@ static struct sa1100fb_mach_info collie_lcd_info = {
+
+ .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+ .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
+
+-#ifdef CONFIG_BACKLIGHT_LOCOMO
+- .lcd_power = locomolcd_power
+-#endif
++static struct iio_map locomo_iio_map[] = {
++ {
++ .consumer_dev_name = "locomo-lcd.0",
++ .consumer_channel = "comadj",
++ .adc_channel_label = "CH0",
++ },
++ { }
++};
++
++static struct i2c_board_info locomo_i2c_devs[] __initdata = {
++ {
++ I2C_BOARD_INFO("m62332", 0x4e),
++ .platform_data = locomo_iio_map,
++ },
++};
++
++static struct gpiod_lookup_table collie_bl_gpios_table = {
++ .dev_id = "locomo-backlight.0",
++ .table = {
++ GPIO_LOOKUP("locomo-gpio", 9, "flvr", GPIO_ACTIVE_HIGH),
++ { },
++ },
++}, collie_lcd_gpios_table = {
++ .dev_id = "locomo-lcd.0",
++ .table = {
++ GPIO_LOOKUP("locomo-gpio", 4, "VSHA", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 5, "VSHD", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 6, "Vee", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 7, "MOD", GPIO_ACTIVE_HIGH),
++ { },
++ },
+ };
+
+ static void __init collie_init(void)
+@@ -381,11 +486,19 @@ static void __init collie_init(void)
+
+ GPSR |= _COLLIE_GPIO_UCB1x00_RESET;
+
++ sharpsl_save_param();
++
+ sa11x0_ppc_configure_mcp();
+
+
+ platform_scoop_config = &collie_pcmcia_config;
+
++ if (sharpsl_param.comadj != -1)
++ locomo_info.comadj = sharpsl_param.comadj;
++
++ gpiod_add_lookup_table(&collie_bl_gpios_table);
++ gpiod_add_lookup_table(&collie_lcd_gpios_table);
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if (ret) {
+ printk(KERN_WARNING "collie: Unable to register LoCoMo device\n");
+@@ -397,7 +510,13 @@ static void __init collie_init(void)
+ sa11x0_register_mcp(&collie_mcp_data);
+ sa11x0_register_irda(&collie_ir_data);
+
+- sharpsl_save_param();
++ i2c_register_board_info(1,
++ locomo_i2c_devs, ARRAY_SIZE(locomo_i2c_devs));
++
++ spi_register_board_info(collie_spi_board_info,
++ ARRAY_SIZE(collie_spi_board_info));
++
++ regulator_has_full_constraints();
+ }
+
+ static struct map_desc collie_io_desc[] __initdata = {
+@@ -419,9 +538,7 @@ static void __init collie_map_io(void)
+ sa1100_map_io();
+ iotable_init(collie_io_desc, ARRAY_SIZE(collie_io_desc));
+
+-#ifdef CONFIG_SHARP_LOCOMO
+ sa1100_register_uart_fns(&collie_port_fns);
+-#endif
+ sa1100_register_uart(0, 3);
+ sa1100_register_uart(1, 1);
+ }
+diff --git a/arch/arm/mach-sa1100/include/mach/collie.h b/arch/arm/mach-sa1100/include/mach/collie.h
+index b478ca1..a66319a 100644
+--- a/arch/arm/mach-sa1100/include/mach/collie.h
++++ b/arch/arm/mach-sa1100/include/mach/collie.h
+@@ -24,12 +24,12 @@ extern void locomolcd_power(int on);
+ #define COLLIE_SCP_MUTE_L SCOOP_GPCR_PA14
+ #define COLLIE_SCP_MUTE_R SCOOP_GPCR_PA15
+ #define COLLIE_SCP_5VON SCOOP_GPCR_PA16
+-#define COLLIE_SCP_AMP_ON SCOOP_GPCR_PA17
++#define COLLIE_GPIO_AMP2_ON (COLLIE_SCOOP_GPIO_BASE + 6)
+ #define COLLIE_GPIO_VPEN (COLLIE_SCOOP_GPIO_BASE + 7)
+ #define COLLIE_SCP_LB_VOL_CHG SCOOP_GPCR_PA19
+
+ #define COLLIE_SCOOP_IO_DIR (COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R | \
+- COLLIE_SCP_5VON | COLLIE_SCP_AMP_ON | \
++ COLLIE_SCP_5VON | \
+ COLLIE_SCP_LB_VOL_CHG)
+ #define COLLIE_SCOOP_IO_OUT (COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R)
+
+@@ -81,7 +81,7 @@ extern void locomolcd_power(int on);
+ #define COLLIE_TC35143_GPIO_TBL_CHK UCB_IO_1
+ #define COLLIE_TC35143_GPIO_VPEN_ON UCB_IO_2
+ #define COLLIE_GPIO_IR_ON (COLLIE_TC35143_GPIO_BASE + 3)
+-#define COLLIE_TC35143_GPIO_AMP_ON UCB_IO_4
++#define COLLIE_GPIO_AMP1_ON (COLLIE_TC35143_GPIO_BASE + 4)
+ #define COLLIE_TC35143_GPIO_VERSION1 UCB_IO_5
+ #define COLLIE_TC35143_GPIO_FS8KLPF UCB_IO_5
+ #define COLLIE_TC35143_GPIO_BUZZER_BIAS UCB_IO_6
+@@ -92,4 +92,23 @@ extern void locomolcd_power(int on);
+ #define COLLIE_TC35143_GPIO_OUT (UCB_IO_1 | UCB_IO_3 | UCB_IO_4 \
+ | UCB_IO_6)
+
++/* GPIOs on LoCoMo GA */
++#define COLLIE_LOCOMO_GPIO_BASE (GPIO_MAX + 23)
++#define COLLIE_GPIO_RTS (COLLIE_LOCOMO_GPIO_BASE + 0)
++#define COLLIE_GPIO_CTS (COLLIE_LOCOMO_GPIO_BASE + 1)
++#define COLLIE_GPIO_DSR (COLLIE_LOCOMO_GPIO_BASE + 2)
++#define COLLIE_GPIO_DTR (COLLIE_LOCOMO_GPIO_BASE + 3)
++#define COLLIE_GPIO_LCD_VSHA_ON (COLLIE_LOCOMO_GPIO_BASE + 4)
++#define COLLIE_GPIO_LCD_VSHD_ON (COLLIE_LOCOMO_GPIO_BASE + 5)
++#define COLLIE_GPIO_LCD_VEE_ON (COLLIE_LOCOMO_GPIO_BASE + 6)
++#define COLLIE_GPIO_LCD_MOD (COLLIE_LOCOMO_GPIO_BASE + 7)
++#define COLLIE_LOCOMO_GPIO_DAC_ON LOCOMO_GPIO(8)
++#define COLLIE_GPIO_FL_VR (COLLIE_LOCOMO_GPIO_BASE + 9)
++#define COLLIE_LOCOMO_GPIO_DAC_SDATA LOCOMO_GPIO(10)
++#define COLLIE_LOCOMO_GPIO_DAC_SCK LOCOMO_GPIO(11)
++#define COLLIE_LOCOMO_GPIO_DAC_SLOAD LOCOMO_GPIO(12)
++#define COLLIE_GPIO_CARD_DETECT (COLLIE_LOCOMO_GPIO_BASE + 13)
++#define COLLIE_GPIO_CARD_RO (COLLIE_LOCOMO_GPIO_BASE + 14)
++#define COLLIE_GPIO_CARD_POWER (COLLIE_LOCOMO_GPIO_BASE + 15)
++
+ #endif
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0015-ARM-sa1100-don-t-preallocate-IRQ-space-for-locomo.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0015-ARM-sa1100-don-t-preallocate-IRQ-space-for-locomo.patch
new file mode 100644
index 0000000..40d68ee
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0015-ARM-sa1100-don-t-preallocate-IRQ-space-for-locomo.patch
@@ -0,0 +1,45 @@
+From 5cbe80e1ddbaf8cb3ac4865d4eaf6b1618ff6c04 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Wed, 6 Nov 2013 01:18:03 +0400
+Subject: [PATCH 15/20] ARM: sa1100: don't preallocate IRQ space for locomo
+
+As new locomo driver properly supports SPARSE_IRQ, stop playing with
+NR_IRQS on sa1100 (locomo was the last chip requiring NR_IRQ tricks).
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-sa1100/include/mach/irqs.h | 19 ++-----------------
+ 1 file changed, 2 insertions(+), 17 deletions(-)
+
+diff --git a/arch/arm/mach-sa1100/include/mach/irqs.h b/arch/arm/mach-sa1100/include/mach/irqs.h
+index 734e30e..b4b9608 100644
+--- a/arch/arm/mach-sa1100/include/mach/irqs.h
++++ b/arch/arm/mach-sa1100/include/mach/irqs.h
+@@ -79,22 +79,7 @@
+ #define IRQ_BOARD_START 61
+ #define IRQ_BOARD_END 77
+
+-/*
+- * Figure out the MAX IRQ number.
+- *
+- * Neponset, SA1111 and UCB1x00 are sparse IRQ aware, so can dynamically
+- * allocate their IRQs above NR_IRQS.
+- *
+- * LoCoMo has 4 additional IRQs, but is not sparse IRQ aware, and so has
+- * to be included in the NR_IRQS calculation.
+- */
+-#ifdef CONFIG_SHARP_LOCOMO
+-#define NR_IRQS_LOCOMO 4
+-#else
+-#define NR_IRQS_LOCOMO 0
+-#endif
+-
+ #ifndef NR_IRQS
+-#define NR_IRQS (IRQ_BOARD_START + NR_IRQS_LOCOMO)
++#define NR_IRQS IRQ_BOARD_START
+ #endif
+-#define SA1100_NR_IRQS (IRQ_BOARD_START + NR_IRQS_LOCOMO)
++#define SA1100_NR_IRQS IRQ_BOARD_START
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0016-ARM-pxa-poodle-use-new-LoCoMo-driver.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0016-ARM-pxa-poodle-use-new-LoCoMo-driver.patch
new file mode 100644
index 0000000..355efc0
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0016-ARM-pxa-poodle-use-new-LoCoMo-driver.patch
@@ -0,0 +1,174 @@
+From cbf023b8fa98ea2f1ebd116d5eef8889ba882dca Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Mon, 11 Nov 2013 04:23:00 +0400
+Subject: [PATCH 16/20] ARM: pxa: poodle: use new LoCoMo driver
+
+Convert poodle to use new mfd-based LoCoMo driver.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/Kconfig | 1 -
+ arch/arm/mach-pxa/include/mach/poodle.h | 16 ++++++---
+ arch/arm/mach-pxa/poodle.c | 58 ++++++++++++++++++++++++++++++---
+ 3 files changed, 65 insertions(+), 10 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
+index 8896e71..a2756b0 100644
+--- a/arch/arm/mach-pxa/Kconfig
++++ b/arch/arm/mach-pxa/Kconfig
+@@ -481,7 +481,6 @@ config MACH_POODLE
+ bool "Enable Sharp SL-5600 (Poodle) Support"
+ depends on PXA_SHARPSL
+ select PXA25x
+- select SHARP_LOCOMO
+
+ config MACH_CORGI
+ bool "Enable Sharp SL-C700 (Corgi) Support"
+diff --git a/arch/arm/mach-pxa/include/mach/poodle.h b/arch/arm/mach-pxa/include/mach/poodle.h
+index b56b193..dd4afae 100644
+--- a/arch/arm/mach-pxa/include/mach/poodle.h
++++ b/arch/arm/mach-pxa/include/mach/poodle.h
+@@ -81,11 +81,17 @@
+ #define POODLE_GPIO_L_LCLK (POODLE_SCOOP_GPIO_BASE + 10)
+ #define POODLE_GPIO_HS_OUT (POODLE_SCOOP_GPIO_BASE + 11)
+
+-#define POODLE_LOCOMO_GPIO_AMP_ON LOCOMO_GPIO(8)
+-#define POODLE_LOCOMO_GPIO_MUTE_L LOCOMO_GPIO(10)
+-#define POODLE_LOCOMO_GPIO_MUTE_R LOCOMO_GPIO(11)
+-#define POODLE_LOCOMO_GPIO_232VCC_ON LOCOMO_GPIO(12)
+-#define POODLE_LOCOMO_GPIO_JK_B LOCOMO_GPIO(13)
++#define POODLE_LOCOMO_GPIO_BASE (PXA_NR_BUILTIN_GPIO + 12)
++#define POODLE_GPIO_LCD_VSHA_ON (POODLE_LOCOMO_GPIO_BASE + 4)
++#define POODLE_GPIO_LCD_VSHD_ON (POODLE_LOCOMO_GPIO_BASE + 5)
++#define POODLE_GPIO_LCD_VEE_ON (POODLE_LOCOMO_GPIO_BASE + 6)
++#define POODLE_GPIO_LCD_MOD (POODLE_LOCOMO_GPIO_BASE + 7)
++#define POODLE_GPIO_AMP_ON (POODLE_LOCOMO_GPIO_BASE + 8)
++#define POODLE_GPIO_FL_VR (POODLE_LOCOMO_GPIO_BASE + 9)
++#define POODLE_GPIO_MUTE_L (POODLE_LOCOMO_GPIO_BASE + 10)
++#define POODLE_GPIO_MUTE_R (POODLE_LOCOMO_GPIO_BASE + 11)
++#define POODLE_GPIO_232VCC_ON (POODLE_LOCOMO_GPIO_BASE + 12)
++#define POODLE_GPIO_JK_B (POODLE_LOCOMO_GPIO_BASE + 13)
+
+ #define POODLE_NR_IRQS (IRQ_BOARD_START + 4) /* 4 for LoCoMo */
+
+diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c
+index 195b112..6002112 100644
+--- a/arch/arm/mach-pxa/poodle.c
++++ b/arch/arm/mach-pxa/poodle.c
+@@ -23,6 +23,7 @@
+ #include <linux/delay.h>
+ #include <linux/mtd/physmap.h>
+ #include <linux/gpio.h>
++#include <linux/gpio/machine.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c/pxa-i2c.h>
+ #include <linux/regulator/machine.h>
+@@ -31,6 +32,8 @@
+ #include <linux/spi/pxa2xx_spi.h>
+ #include <linux/mtd/sharpsl.h>
+ #include <linux/memblock.h>
++#include <linux/iio/machine.h>
++#include <linux/mfd/locomo.h>
+
+ #include <mach/hardware.h>
+ #include <asm/mach-types.h>
+@@ -49,7 +52,6 @@
+ #include <linux/platform_data/video-pxafb.h>
+
+ #include <asm/hardware/scoop.h>
+-#include <asm/hardware/locomo.h>
+ #include <asm/mach/sharpsl_param.h>
+
+ #include "generic.h"
+@@ -179,7 +181,8 @@ static struct resource locomo_resources[] = {
+ };
+
+ static struct locomo_platform_data locomo_info = {
+- .irq_base = IRQ_BOARD_START,
++ .gpio_base = POODLE_LOCOMO_GPIO_BASE,
++ .comadj = 118,
+ };
+
+ struct platform_device poodle_locomo_device = {
+@@ -192,8 +195,6 @@ struct platform_device poodle_locomo_device = {
+ },
+ };
+
+-EXPORT_SYMBOL(poodle_locomo_device);
+-
+ #if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE)
+ static struct pxa2xx_spi_master poodle_spi_info = {
+ .num_chipselect = 1,
+@@ -424,6 +425,47 @@ static struct i2c_board_info __initdata poodle_i2c_devices[] = {
+ { I2C_BOARD_INFO("wm8731", 0x1b) },
+ };
+
++static struct iio_map locomo_iio_map[] = {
++ {
++ .consumer_dev_name = "locomo-lcd.0",
++ .consumer_channel = "comadj",
++ .adc_channel_label = "CH0",
++ },
++ { }
++};
++
++static struct i2c_board_info locomo_i2c_devs[] __initdata = {
++ {
++ I2C_BOARD_INFO("m62332", 0x4e),
++ .platform_data = locomo_iio_map,
++ },
++};
++
++static struct gpiod_lookup_table poodle_audio_gpios_table = {
++ .dev_id = "poodle-audio",
++ .table = {
++ GPIO_LOOKUP("locomo-gpio", 10, "mute-l", GPIO_ACTIVE_LOW),
++ GPIO_LOOKUP("locomo-gpio", 11, "mute-r", GPIO_ACTIVE_LOW),
++ GPIO_LOOKUP("locomo-gpio", 8, "amp-on", GPIO_ACTIVE_LOW),
++ { },
++ },
++}, poodle_bl_gpios_table = {
++ .dev_id = "locomo-backlight.0",
++ .table = {
++ GPIO_LOOKUP("locomo-gpio", 9, "flvr", GPIO_ACTIVE_HIGH),
++ { },
++ },
++}, poodle_lcd_gpios_table = {
++ .dev_id = "locomo-lcd.0",
++ .table = {
++ GPIO_LOOKUP("locomo-gpio", 4, "VSHA", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 5, "VSHD", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 6, "Vee", GPIO_ACTIVE_HIGH),
++ GPIO_LOOKUP("locomo-gpio", 7, "MOD", GPIO_ACTIVE_HIGH),
++ { },
++ },
++};
++
+ static void poodle_poweroff(void)
+ {
+ pxa_restart(REBOOT_HARD, NULL);
+@@ -445,6 +487,13 @@ static void __init poodle_init(void)
+
+ platform_scoop_config = &poodle_pcmcia_config;
+
++ if (sharpsl_param.comadj != -1)
++ locomo_info.comadj = sharpsl_param.comadj;
++
++ gpiod_add_lookup_table(&poodle_audio_gpios_table);
++ gpiod_add_lookup_table(&poodle_bl_gpios_table);
++ gpiod_add_lookup_table(&poodle_lcd_gpios_table);
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if (ret)
+ pr_warn("poodle: Unable to register LoCoMo device\n");
+@@ -455,6 +504,7 @@ static void __init poodle_init(void)
+ pxa_set_ficp_info(&poodle_ficp_platform_data);
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(poodle_i2c_devices));
++ i2c_register_board_info(1, ARRAY_AND_SIZE(locomo_i2c_devs));
+ poodle_init_spi();
+ regulator_has_full_constraints();
+ }
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0017-ARM-pxa-poodle-don-t-preallocate-IRQ-space-for-locom.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0017-ARM-pxa-poodle-don-t-preallocate-IRQ-space-for-locom.patch
new file mode 100644
index 0000000..dbaf549
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0017-ARM-pxa-poodle-don-t-preallocate-IRQ-space-for-locom.patch
@@ -0,0 +1,30 @@
+From 9b81cda92cccc3f875a5a32e112742d81fd109d8 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Wed, 6 Nov 2013 01:21:01 +0400
+Subject: [PATCH 17/20] ARM: pxa: poodle: don't preallocate IRQ space for
+ locomo
+
+As new locomo driver supports SPARSE_IRQ, don't preallocate NR_IRQS
+space for it on poodle.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/include/mach/poodle.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/mach-pxa/include/mach/poodle.h b/arch/arm/mach-pxa/include/mach/poodle.h
+index dd4afae..1fdde6b 100644
+--- a/arch/arm/mach-pxa/include/mach/poodle.h
++++ b/arch/arm/mach-pxa/include/mach/poodle.h
+@@ -93,7 +93,7 @@
+ #define POODLE_GPIO_232VCC_ON (POODLE_LOCOMO_GPIO_BASE + 12)
+ #define POODLE_GPIO_JK_B (POODLE_LOCOMO_GPIO_BASE + 13)
+
+-#define POODLE_NR_IRQS (IRQ_BOARD_START + 4) /* 4 for LoCoMo */
++#define POODLE_NR_IRQS IRQ_BOARD_START
+
+ extern struct platform_device poodle_locomo_device;
+
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0018-ASoC-pxa-poodle-make-use-of-new-locomo-GPIO-interfac.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0018-ASoC-pxa-poodle-make-use-of-new-locomo-GPIO-interfac.patch
new file mode 100644
index 0000000..3d78ec4
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0018-ASoC-pxa-poodle-make-use-of-new-locomo-GPIO-interfac.patch
@@ -0,0 +1,120 @@
+From ec10362336c5c49c14c1490600a0b6a01460cb83 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Fri, 24 Oct 2014 23:35:07 +0400
+Subject: [PATCH 18/20] ASoC: pxa: poodle: make use of new locomo GPIO
+ interface
+
+Since LoCoMo driver has been converted to provide proper gpiolib
+interface, make poodle ASoC platform driver use gpiolib API.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Acked-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/pxa/poodle.c | 52 +++++++++++++++++++-------------------------------
+ 1 file changed, 20 insertions(+), 32 deletions(-)
+
+diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
+index 0fce8c4..c5b7c5e 100644
+--- a/sound/soc/pxa/poodle.c
++++ b/sound/soc/pxa/poodle.c
+@@ -20,15 +20,11 @@
+ #include <linux/i2c.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
++#include <linux/gpio/consumer.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+
+-#include <asm/mach-types.h>
+-#include <asm/hardware/locomo.h>
+-#include <mach/poodle.h>
+-#include <mach/audio.h>
+-
+ #include "../codecs/wm8731.h"
+ #include "pxa2xx-i2s.h"
+
+@@ -42,22 +38,18 @@
+
+ static int poodle_jack_func;
+ static int poodle_spk_func;
++static struct gpio_desc *poodle_mute_l, *poodle_mute_r, *poodle_amp_on;
+
+ static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
+ {
+ /* set up jack connection */
+ if (poodle_jack_func == POODLE_HP) {
+- /* set = unmute headphone */
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_L, 1);
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_R, 1);
++ gpiod_set_value(poodle_mute_l, 0);
++ gpiod_set_value(poodle_mute_r, 0);
+ snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+ } else {
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_L, 0);
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_R, 0);
++ gpiod_set_value(poodle_mute_l, 1);
++ gpiod_set_value(poodle_mute_r, 1);
+ snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+ }
+
+@@ -84,11 +76,8 @@ static int poodle_startup(struct snd_pcm_substream *substream)
+ /* we need to unmute the HP at shutdown as the mute burns power on poodle */
+ static void poodle_shutdown(struct snd_pcm_substream *substream)
+ {
+- /* set = unmute headphone */
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_L, 1);
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_R, 1);
++ gpiod_set_value(poodle_mute_l, 0);
++ gpiod_set_value(poodle_mute_r, 0);
+ }
+
+ static int poodle_hw_params(struct snd_pcm_substream *substream,
+@@ -178,12 +167,7 @@ static int poodle_set_spk(struct snd_kcontrol *kcontrol,
+ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+ {
+- if (SND_SOC_DAPM_EVENT_ON(event))
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_AMP_ON, 0);
+- else
+- locomo_gpio_write(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_AMP_ON, 1);
++ gpiod_set_value(poodle_amp_on, (SND_SOC_DAPM_EVENT_ON(event)));
+
+ return 0;
+ }
+@@ -268,13 +252,17 @@ static int poodle_probe(struct platform_device *pdev)
+ struct snd_soc_card *card = &poodle;
+ int ret;
+
+- locomo_gpio_set_dir(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_AMP_ON, 0);
+- /* should we mute HP at startup - burning power ?*/
+- locomo_gpio_set_dir(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_L, 0);
+- locomo_gpio_set_dir(&poodle_locomo_device.dev,
+- POODLE_LOCOMO_GPIO_MUTE_R, 0);
++ poodle_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_HIGH);
++ if (IS_ERR(poodle_mute_l))
++ return PTR_ERR(poodle_mute_l);
++
++ poodle_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_HIGH);
++ if (IS_ERR(poodle_mute_r))
++ return PTR_ERR(poodle_mute_l);
++
++ poodle_amp_on = devm_gpiod_get(&pdev->dev, "amp-on", GPIOD_OUT_LOW);
++ if (IS_ERR(poodle_amp_on))
++ return PTR_ERR(poodle_amp_on);
+
+ card->dev = &pdev->dev;
+
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0019-poodle-gpio-uart-hack.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0019-poodle-gpio-uart-hack.patch
new file mode 100644
index 0000000..c42f21b
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0019-poodle-gpio-uart-hack.patch
@@ -0,0 +1,26 @@
+From 8bab2ab9f758aa15ea1f97e4c11f27bcd094b4d1 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Fri, 28 Nov 2014 15:27:51 +0300
+Subject: [PATCH 19/20] poodle gpio uart hack
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ drivers/gpio/gpio-locomo.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpio/gpio-locomo.c b/drivers/gpio/gpio-locomo.c
+index d8e5880..c8510fa 100644
+--- a/drivers/gpio/gpio-locomo.c
++++ b/drivers/gpio/gpio-locomo.c
+@@ -224,7 +224,7 @@ static int locomo_gpio_probe(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, lg);
+
+- regmap_write(lg->regmap, LOCOMO_GPO, 0x00);
++ regmap_write(lg->regmap, LOCOMO_GPO, 1 << 12);
+ regmap_write(lg->regmap, LOCOMO_GPE, 0x00);
+ regmap_write(lg->regmap, LOCOMO_GPD, 0x00);
+ regmap_write(lg->regmap, LOCOMO_GIE, 0x00);
+--
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-handheld-4.0/locomo/0020-irq-HACK-around-for-handling-irq0-when-we-should.patch b/recipes-kernel/linux/linux-handheld-4.0/locomo/0020-irq-HACK-around-for-handling-irq0-when-we-should.patch
new file mode 100644
index 0000000..afbc61b
--- /dev/null
+++ b/recipes-kernel/linux/linux-handheld-4.0/locomo/0020-irq-HACK-around-for-handling-irq0-when-we-should.patch
@@ -0,0 +1,29 @@
+From c87b3232a374940cf5cf45242eeae2da48395799 Mon Sep 17 00:00:00 2001
+From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+Date: Wed, 24 Dec 2014 12:27:21 +0300
+Subject: [PATCH 20/20] irq: HACK around for handling irq0 when we should
+
+Instead of backporting large patches changing IRQ handling on sa1100,
+just make handle_domain_irq handle irq 0 as usual.
+
+Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+---
+ kernel/irq/irqdesc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index 99793b9..06ca4e6 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -381,7 +381,7 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
+ * Some hardware gives randomly wrong interrupts. Rather
+ * than crashing, do something sensible.
+ */
+- if (unlikely(!irq || irq >= nr_irqs)) {
++ if (unlikely(/*!irq || */irq >= nr_irqs)) {
+ ack_bad_irq(irq);
+ ret = -EINVAL;
+ } else {
+--
+1.9.1
+