2007-02-21 04:58:01 +07:00
|
|
|
/* linux/drivers/mfd/sm501.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Simtec Electronics
|
|
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
|
|
* Vincent Sanders <vince@simtec.co.uk>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* SM501 MFD driver
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/pci.h>
|
2018-04-20 03:00:07 +07:00
|
|
|
#include <linux/platform_data/i2c-gpio.h>
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
#include <linux/gpio/machine.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
#include <linux/sm501.h>
|
|
|
|
#include <linux/sm501-regs.h>
|
2008-04-28 16:14:22 +07:00
|
|
|
#include <linux/serial_8250.h>
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2010-11-10 14:47:51 +07:00
|
|
|
#include <linux/io.h>
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
struct sm501_device {
|
|
|
|
struct list_head list;
|
|
|
|
struct platform_device pdev;
|
|
|
|
};
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio;
|
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
#ifdef CONFIG_MFD_SM501_GPIO
|
|
|
|
#include <linux/gpio.h>
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio_chip {
|
|
|
|
struct gpio_chip gpio;
|
|
|
|
struct sm501_gpio *ourgpio; /* to get back to parent. */
|
|
|
|
void __iomem *regbase;
|
2009-01-10 22:59:53 +07:00
|
|
|
void __iomem *control; /* address of control reg. */
|
2008-07-25 15:45:59 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct sm501_gpio {
|
|
|
|
struct sm501_gpio_chip low;
|
|
|
|
struct sm501_gpio_chip high;
|
|
|
|
spinlock_t lock;
|
|
|
|
|
|
|
|
unsigned int registered : 1;
|
|
|
|
void __iomem *regs;
|
|
|
|
struct resource *regs_res;
|
|
|
|
};
|
2008-07-25 15:46:02 +07:00
|
|
|
#else
|
|
|
|
struct sm501_gpio {
|
|
|
|
/* no gpio support, empty definition for sm501_devdata. */
|
|
|
|
};
|
|
|
|
#endif
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
struct sm501_devdata {
|
|
|
|
spinlock_t reg_lock;
|
|
|
|
struct mutex clock_lock;
|
|
|
|
struct list_head devices;
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio gpio;
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
struct device *dev;
|
|
|
|
struct resource *io_res;
|
|
|
|
struct resource *mem_res;
|
|
|
|
struct resource *regs_claim;
|
|
|
|
struct sm501_platdata *platdata;
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
unsigned int in_suspend;
|
|
|
|
unsigned long pm_misc;
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
int unit_power[20];
|
|
|
|
unsigned int pdev_id;
|
|
|
|
unsigned int irq;
|
|
|
|
void __iomem *regs;
|
2008-03-05 05:28:50 +07:00
|
|
|
unsigned int rev;
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
#define MHZ (1000 * 1000)
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2008-03-05 05:28:49 +07:00
|
|
|
static const unsigned int div_tab[] = {
|
2007-02-21 04:58:01 +07:00
|
|
|
[0] = 1,
|
|
|
|
[1] = 2,
|
|
|
|
[2] = 4,
|
|
|
|
[3] = 8,
|
|
|
|
[4] = 16,
|
|
|
|
[5] = 32,
|
|
|
|
[6] = 64,
|
|
|
|
[7] = 128,
|
|
|
|
[8] = 3,
|
|
|
|
[9] = 6,
|
|
|
|
[10] = 12,
|
|
|
|
[11] = 24,
|
|
|
|
[12] = 48,
|
|
|
|
[13] = 96,
|
|
|
|
[14] = 192,
|
|
|
|
[15] = 384,
|
|
|
|
[16] = 5,
|
|
|
|
[17] = 10,
|
|
|
|
[18] = 20,
|
|
|
|
[19] = 40,
|
|
|
|
[20] = 80,
|
|
|
|
[21] = 160,
|
|
|
|
[22] = 320,
|
|
|
|
[23] = 604,
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned long decode_div(unsigned long pll2, unsigned long val,
|
|
|
|
unsigned int lshft, unsigned int selbit,
|
2008-03-05 05:28:49 +07:00
|
|
|
unsigned long mask)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
|
|
|
if (val & selbit)
|
|
|
|
pll2 = 288 * MHZ;
|
|
|
|
|
2008-03-05 05:28:49 +07:00
|
|
|
return pll2 / div_tab[(val >> lshft) & mask];
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x)
|
|
|
|
|
|
|
|
/* sm501_dump_clk
|
|
|
|
*
|
|
|
|
* Print out the current clock configuration for the device
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_dump_clk(struct sm501_devdata *sm)
|
|
|
|
{
|
2011-01-24 16:57:20 +07:00
|
|
|
unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING);
|
|
|
|
unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
|
|
|
|
unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
|
|
|
|
unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
|
2007-02-21 04:58:01 +07:00
|
|
|
unsigned long sdclk0, sdclk1;
|
|
|
|
unsigned long pll2 = 0;
|
|
|
|
|
|
|
|
switch (misct & 0x30) {
|
|
|
|
case 0x00:
|
|
|
|
pll2 = 336 * MHZ;
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
pll2 = 288 * MHZ;
|
|
|
|
break;
|
|
|
|
case 0x20:
|
|
|
|
pll2 = 240 * MHZ;
|
|
|
|
break;
|
|
|
|
case 0x30:
|
|
|
|
pll2 = 192 * MHZ;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ;
|
2008-03-05 05:28:49 +07:00
|
|
|
sdclk0 /= div_tab[((misct >> 8) & 0xf)];
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ;
|
2008-03-05 05:28:49 +07:00
|
|
|
sdclk1 /= div_tab[((misct >> 16) & 0xf)];
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n",
|
|
|
|
misct, pm0, pm1);
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n",
|
|
|
|
fmt_freq(pll2), sdclk0, sdclk1);
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1);
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "PM0[%c]: "
|
|
|
|
"P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
|
2007-11-29 07:21:37 +07:00
|
|
|
"M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
|
2007-02-21 04:58:01 +07:00
|
|
|
(pmc & 3 ) == 0 ? '*' : '-',
|
2008-03-05 05:28:49 +07:00
|
|
|
fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31)),
|
|
|
|
fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15)),
|
|
|
|
fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15)),
|
|
|
|
fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15)));
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
dev_dbg(sm->dev, "PM1[%c]: "
|
|
|
|
"P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
|
|
|
|
"M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
|
|
|
|
(pmc & 3 ) == 1 ? '*' : '-',
|
2008-03-05 05:28:49 +07:00
|
|
|
fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31)),
|
|
|
|
fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15)),
|
|
|
|
fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15)),
|
|
|
|
fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15)));
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
2007-06-24 07:16:28 +07:00
|
|
|
|
|
|
|
static void sm501_dump_regs(struct sm501_devdata *sm)
|
|
|
|
{
|
|
|
|
void __iomem *regs = sm->regs;
|
|
|
|
|
|
|
|
dev_info(sm->dev, "System Control %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_SYSTEM_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "Misc Control %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_MISC_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "GPIO Control Low %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_GPIO31_0_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "GPIO Control Hi %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_GPIO63_32_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "DRAM Control %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_DRAM_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "Arbitration Ctrl %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_ARBTRTN_CONTROL));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "Misc Timing %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(regs + SM501_MISC_TIMING));
|
2007-06-24 07:16:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sm501_dump_gate(struct sm501_devdata *sm)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "CurrentGate %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(sm->regs + SM501_CURRENT_GATE));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "CurrentClock %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(sm->regs + SM501_CURRENT_CLOCK));
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "PowerModeControl %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL));
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
2007-06-24 07:16:28 +07:00
|
|
|
|
|
|
|
#else
|
|
|
|
static inline void sm501_dump_gate(struct sm501_devdata *sm) { }
|
|
|
|
static inline void sm501_dump_regs(struct sm501_devdata *sm) { }
|
|
|
|
static inline void sm501_dump_clk(struct sm501_devdata *sm) { }
|
2007-02-21 04:58:01 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* sm501_sync_regs
|
|
|
|
*
|
|
|
|
* ensure the
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_sync_regs(struct sm501_devdata *sm)
|
|
|
|
{
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_readl(sm->regs);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
|
|
|
|
{
|
|
|
|
/* during suspend/resume, we are currently not allowed to sleep,
|
|
|
|
* so change to using mdelay() instead of msleep() if we
|
|
|
|
* are in one of these paths */
|
|
|
|
|
|
|
|
if (sm->in_suspend)
|
|
|
|
mdelay(delay);
|
|
|
|
else
|
|
|
|
msleep(delay);
|
|
|
|
}
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
/* sm501_misc_control
|
|
|
|
*
|
2007-06-24 07:16:28 +07:00
|
|
|
* alters the miscellaneous control parameters
|
2007-02-21 04:58:01 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
int sm501_misc_control(struct device *dev,
|
|
|
|
unsigned long set, unsigned long clear)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev);
|
|
|
|
unsigned long misc;
|
|
|
|
unsigned long save;
|
|
|
|
unsigned long to;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sm->reg_lock, save);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
|
2007-02-21 04:58:01 +07:00
|
|
|
to = (misc & ~clear) | set;
|
|
|
|
|
|
|
|
if (to != misc) {
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(to, sm->regs + SM501_MISC_CONTROL);
|
2007-02-21 04:58:01 +07:00
|
|
|
sm501_sync_regs(sm);
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sm->reg_lock, save);
|
|
|
|
return to;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sm501_misc_control);
|
|
|
|
|
|
|
|
/* sm501_modify_reg
|
|
|
|
*
|
|
|
|
* Modify a register in the SM501 which may be shared with other
|
|
|
|
* drivers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned long sm501_modify_reg(struct device *dev,
|
|
|
|
unsigned long reg,
|
|
|
|
unsigned long set,
|
|
|
|
unsigned long clear)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev);
|
|
|
|
unsigned long data;
|
|
|
|
unsigned long save;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sm->reg_lock, save);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
data = smc501_readl(sm->regs + reg);
|
2007-02-21 04:58:01 +07:00
|
|
|
data |= set;
|
|
|
|
data &= ~clear;
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(data, sm->regs + reg);
|
2007-02-21 04:58:01 +07:00
|
|
|
sm501_sync_regs(sm);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sm->reg_lock, save);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sm501_modify_reg);
|
|
|
|
|
|
|
|
/* sm501_unit_power
|
|
|
|
*
|
|
|
|
* alters the power active gate to set specific units on or off
|
|
|
|
*/
|
|
|
|
|
|
|
|
int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev);
|
|
|
|
unsigned long mode;
|
|
|
|
unsigned long gate;
|
|
|
|
unsigned long clock;
|
|
|
|
|
|
|
|
mutex_lock(&sm->clock_lock);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
|
|
|
|
gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
|
|
|
|
clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
mode &= 3; /* get current power mode */
|
|
|
|
|
2007-04-02 13:49:38 +07:00
|
|
|
if (unit >= ARRAY_SIZE(sm->unit_power)) {
|
2008-04-30 14:54:57 +07:00
|
|
|
dev_err(dev, "%s: bad unit %d\n", __func__, unit);
|
2007-02-21 04:58:01 +07:00
|
|
|
goto already;
|
|
|
|
}
|
|
|
|
|
2008-04-30 14:54:57 +07:00
|
|
|
dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit,
|
2007-02-21 04:58:01 +07:00
|
|
|
sm->unit_power[unit], to);
|
|
|
|
|
|
|
|
if (to == 0 && sm->unit_power[unit] == 0) {
|
|
|
|
dev_err(sm->dev, "unit %d is already shutdown\n", unit);
|
|
|
|
goto already;
|
|
|
|
}
|
|
|
|
|
|
|
|
sm->unit_power[unit] += to ? 1 : -1;
|
|
|
|
to = sm->unit_power[unit] ? 1 : 0;
|
|
|
|
|
|
|
|
if (to) {
|
|
|
|
if (gate & (1 << unit))
|
|
|
|
goto already;
|
|
|
|
gate |= (1 << unit);
|
|
|
|
} else {
|
|
|
|
if (!(gate & (1 << unit)))
|
|
|
|
goto already;
|
|
|
|
gate &= ~(1 << unit);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case 1:
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
|
|
|
|
smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
mode = 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 0:
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
|
|
|
|
smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
mode = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-06-22 05:00:02 +07:00
|
|
|
gate = -1;
|
|
|
|
goto already;
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
|
2007-02-21 04:58:01 +07:00
|
|
|
sm501_sync_regs(sm);
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
|
|
|
|
gate, clock, mode);
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
sm501_mdelay(sm, 16);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
already:
|
|
|
|
mutex_unlock(&sm->clock_lock);
|
|
|
|
return gate;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sm501_unit_power);
|
|
|
|
|
|
|
|
/* clock value structure. */
|
|
|
|
struct sm501_clock {
|
|
|
|
unsigned long mclk;
|
|
|
|
int divider;
|
|
|
|
int shift;
|
2008-03-05 05:28:50 +07:00
|
|
|
unsigned int m, n, k;
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
2008-03-05 05:28:50 +07:00
|
|
|
/* sm501_calc_clock
|
|
|
|
*
|
|
|
|
* Calculates the nearest discrete clock frequency that
|
|
|
|
* can be achieved with the specified input clock.
|
|
|
|
* the maximum divisor is 3 or 5
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int sm501_calc_clock(unsigned long freq,
|
|
|
|
struct sm501_clock *clock,
|
|
|
|
int max_div,
|
|
|
|
unsigned long mclk,
|
|
|
|
long *best_diff)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int divider;
|
|
|
|
int shift;
|
|
|
|
long diff;
|
|
|
|
|
|
|
|
/* try dividers 1 and 3 for CRT and for panel,
|
|
|
|
try divider 5 for panel only.*/
|
|
|
|
|
|
|
|
for (divider = 1; divider <= max_div; divider += 2) {
|
|
|
|
/* try all 8 shift values.*/
|
|
|
|
for (shift = 0; shift < 8; shift++) {
|
|
|
|
/* Calculate difference to requested clock */
|
2012-03-12 09:12:34 +07:00
|
|
|
diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq;
|
2008-03-05 05:28:50 +07:00
|
|
|
if (diff < 0)
|
|
|
|
diff = -diff;
|
|
|
|
|
|
|
|
/* If it is less than the current, use it */
|
|
|
|
if (diff < *best_diff) {
|
|
|
|
*best_diff = diff;
|
|
|
|
|
|
|
|
clock->mclk = mclk;
|
|
|
|
clock->divider = divider;
|
|
|
|
clock->shift = shift;
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_calc_pll
|
|
|
|
*
|
|
|
|
* Calculates the nearest discrete clock frequency that can be
|
|
|
|
* achieved using the programmable PLL.
|
|
|
|
* the maximum divisor is 3 or 5
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned long sm501_calc_pll(unsigned long freq,
|
|
|
|
struct sm501_clock *clock,
|
|
|
|
int max_div)
|
|
|
|
{
|
|
|
|
unsigned long mclk;
|
|
|
|
unsigned int m, n, k;
|
|
|
|
long best_diff = 999999999;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The SM502 datasheet doesn't specify the min/max values for M and N.
|
|
|
|
* N = 1 at least doesn't work in practice.
|
|
|
|
*/
|
|
|
|
for (m = 2; m <= 255; m++) {
|
|
|
|
for (n = 2; n <= 127; n++) {
|
|
|
|
for (k = 0; k <= 1; k++) {
|
|
|
|
mclk = (24000000UL * m / n) >> k;
|
|
|
|
|
|
|
|
if (sm501_calc_clock(freq, clock, max_div,
|
|
|
|
mclk, &best_diff)) {
|
|
|
|
clock->m = m;
|
|
|
|
clock->n = n;
|
|
|
|
clock->k = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return best clock. */
|
|
|
|
return clock->mclk / (clock->divider << clock->shift);
|
|
|
|
}
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
/* sm501_select_clock
|
|
|
|
*
|
2008-03-05 05:28:50 +07:00
|
|
|
* Calculates the nearest discrete clock frequency that can be
|
|
|
|
* achieved using the 288MHz and 336MHz PLLs.
|
2007-02-21 04:58:01 +07:00
|
|
|
* the maximum divisor is 3 or 5
|
|
|
|
*/
|
2008-03-05 05:28:50 +07:00
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
static unsigned long sm501_select_clock(unsigned long freq,
|
|
|
|
struct sm501_clock *clock,
|
|
|
|
int max_div)
|
|
|
|
{
|
|
|
|
unsigned long mclk;
|
|
|
|
long best_diff = 999999999;
|
|
|
|
|
|
|
|
/* Try 288MHz and 336MHz clocks. */
|
|
|
|
for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) {
|
2008-03-05 05:28:50 +07:00
|
|
|
sm501_calc_clock(freq, clock, max_div, mclk, &best_diff);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return best clock. */
|
|
|
|
return clock->mclk / (clock->divider << clock->shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_set_clock
|
|
|
|
*
|
|
|
|
* set one of the four clock sources to the closest available frequency to
|
|
|
|
* the one specified
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned long sm501_set_clock(struct device *dev,
|
|
|
|
int clksrc,
|
|
|
|
unsigned long req_freq)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev);
|
2011-01-24 16:57:20 +07:00
|
|
|
unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
|
|
|
|
unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
|
|
|
|
unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
|
2008-03-05 05:28:50 +07:00
|
|
|
unsigned int pll_reg = 0;
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
unsigned long sm501_freq; /* the actual frequency achieved */
|
2014-08-18 22:11:27 +07:00
|
|
|
u64 reg;
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
struct sm501_clock to;
|
|
|
|
|
|
|
|
/* find achivable discrete frequency and setup register value
|
|
|
|
* accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK
|
|
|
|
* has an extra bit for the divider */
|
|
|
|
|
|
|
|
switch (clksrc) {
|
|
|
|
case SM501_CLOCK_P2XCLK:
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
/* This clock is divided in half so to achieve the
|
2007-02-21 04:58:01 +07:00
|
|
|
* requested frequency the value must be multiplied by
|
|
|
|
* 2. This clock also has an additional pre divisor */
|
|
|
|
|
2008-03-05 05:28:50 +07:00
|
|
|
if (sm->rev >= 0xC0) {
|
|
|
|
/* SM502 -> use the programmable PLL */
|
|
|
|
sm501_freq = (sm501_calc_pll(2 * req_freq,
|
|
|
|
&to, 5) / 2);
|
|
|
|
reg = to.shift & 0x07;/* bottom 3 bits are shift */
|
|
|
|
if (to.divider == 3)
|
|
|
|
reg |= 0x08; /* /3 divider required */
|
|
|
|
else if (to.divider == 5)
|
|
|
|
reg |= 0x10; /* /5 divider required */
|
|
|
|
reg |= 0x40; /* select the programmable PLL */
|
|
|
|
pll_reg = 0x20000 | (to.k << 15) | (to.n << 8) | to.m;
|
|
|
|
} else {
|
|
|
|
sm501_freq = (sm501_select_clock(2 * req_freq,
|
|
|
|
&to, 5) / 2);
|
|
|
|
reg = to.shift & 0x07;/* bottom 3 bits are shift */
|
|
|
|
if (to.divider == 3)
|
|
|
|
reg |= 0x08; /* /3 divider required */
|
|
|
|
else if (to.divider == 5)
|
|
|
|
reg |= 0x10; /* /5 divider required */
|
|
|
|
if (to.mclk != 288000000)
|
|
|
|
reg |= 0x20; /* which mclk pll is source */
|
|
|
|
}
|
2007-02-21 04:58:01 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SM501_CLOCK_V2XCLK:
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
/* This clock is divided in half so to achieve the
|
2007-02-21 04:58:01 +07:00
|
|
|
* requested frequency the value must be multiplied by 2. */
|
|
|
|
|
|
|
|
sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
|
|
|
|
reg=to.shift & 0x07; /* bottom 3 bits are shift */
|
|
|
|
if (to.divider == 3)
|
|
|
|
reg |= 0x08; /* /3 divider required */
|
|
|
|
if (to.mclk != 288000000)
|
|
|
|
reg |= 0x10; /* which mclk pll is source */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SM501_CLOCK_MCLK:
|
|
|
|
case SM501_CLOCK_M1XCLK:
|
|
|
|
/* These clocks are the same and not further divided */
|
|
|
|
|
|
|
|
sm501_freq = sm501_select_clock( req_freq, &to, 3);
|
|
|
|
reg=to.shift & 0x07; /* bottom 3 bits are shift */
|
|
|
|
if (to.divider == 3)
|
|
|
|
reg |= 0x08; /* /3 divider required */
|
|
|
|
if (to.mclk != 288000000)
|
|
|
|
reg |= 0x10; /* which mclk pll is source */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0; /* this is bad */
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&sm->clock_lock);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
|
|
|
|
gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
|
|
|
|
clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
clock = clock & ~(0xFF << clksrc);
|
|
|
|
clock |= reg<<clksrc;
|
|
|
|
|
|
|
|
mode &= 3; /* find current mode */
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case 1:
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
|
|
|
|
smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
mode = 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 0:
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
|
|
|
|
smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
|
2007-02-21 04:58:01 +07:00
|
|
|
mode = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
mutex_unlock(&sm->clock_lock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
|
2008-03-05 05:28:50 +07:00
|
|
|
|
|
|
|
if (pll_reg)
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(pll_reg,
|
|
|
|
sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL);
|
2008-03-05 05:28:50 +07:00
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
sm501_sync_regs(sm);
|
|
|
|
|
2008-08-11 04:45:24 +07:00
|
|
|
dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
|
|
|
|
gate, clock, mode);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
sm501_mdelay(sm, 16);
|
2007-02-21 04:58:01 +07:00
|
|
|
mutex_unlock(&sm->clock_lock);
|
|
|
|
|
|
|
|
sm501_dump_clk(sm);
|
|
|
|
|
|
|
|
return sm501_freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sm501_set_clock);
|
|
|
|
|
|
|
|
/* sm501_find_clock
|
|
|
|
*
|
|
|
|
* finds the closest available frequency for a given clock
|
|
|
|
*/
|
|
|
|
|
2008-03-05 05:28:50 +07:00
|
|
|
unsigned long sm501_find_clock(struct device *dev,
|
|
|
|
int clksrc,
|
2007-02-21 04:58:01 +07:00
|
|
|
unsigned long req_freq)
|
|
|
|
{
|
2008-03-05 05:28:50 +07:00
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev);
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
unsigned long sm501_freq; /* the frequency achieveable by the 501 */
|
2007-02-21 04:58:01 +07:00
|
|
|
struct sm501_clock to;
|
|
|
|
|
|
|
|
switch (clksrc) {
|
|
|
|
case SM501_CLOCK_P2XCLK:
|
2008-03-05 05:28:50 +07:00
|
|
|
if (sm->rev >= 0xC0) {
|
|
|
|
/* SM502 -> use the programmable PLL */
|
|
|
|
sm501_freq = (sm501_calc_pll(2 * req_freq,
|
|
|
|
&to, 5) / 2);
|
|
|
|
} else {
|
|
|
|
sm501_freq = (sm501_select_clock(2 * req_freq,
|
|
|
|
&to, 5) / 2);
|
|
|
|
}
|
2007-02-21 04:58:01 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SM501_CLOCK_V2XCLK:
|
|
|
|
sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SM501_CLOCK_MCLK:
|
|
|
|
case SM501_CLOCK_M1XCLK:
|
|
|
|
sm501_freq = sm501_select_clock(req_freq, &to, 3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sm501_freq = 0; /* error */
|
|
|
|
}
|
|
|
|
|
|
|
|
return sm501_freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sm501_find_clock);
|
|
|
|
|
|
|
|
static struct sm501_device *to_sm_device(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
return container_of(pdev, struct sm501_device, pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_device_release
|
|
|
|
*
|
|
|
|
* A release function for the platform devices we create to allow us to
|
|
|
|
* free any items we allocated
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_device_release(struct device *dev)
|
|
|
|
{
|
|
|
|
kfree(to_sm_device(to_platform_device(dev)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_create_subdev
|
|
|
|
*
|
|
|
|
* Create a skeleton platform device with resources for passing to a
|
|
|
|
* sub-driver
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct platform_device *
|
2008-04-28 16:14:22 +07:00
|
|
|
sm501_create_subdev(struct sm501_devdata *sm, char *name,
|
|
|
|
unsigned int res_count, unsigned int platform_data_size)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
|
|
|
struct sm501_device *smdev;
|
|
|
|
|
|
|
|
smdev = kzalloc(sizeof(struct sm501_device) +
|
2008-04-28 16:14:22 +07:00
|
|
|
(sizeof(struct resource) * res_count) +
|
|
|
|
platform_data_size, GFP_KERNEL);
|
2007-02-21 04:58:01 +07:00
|
|
|
if (!smdev)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
smdev->pdev.dev.release = sm501_device_release;
|
|
|
|
|
|
|
|
smdev->pdev.name = name;
|
|
|
|
smdev->pdev.id = sm->pdev_id;
|
|
|
|
smdev->pdev.dev.parent = sm->dev;
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
if (res_count) {
|
|
|
|
smdev->pdev.resource = (struct resource *)(smdev+1);
|
|
|
|
smdev->pdev.num_resources = res_count;
|
|
|
|
}
|
|
|
|
if (platform_data_size)
|
|
|
|
smdev->pdev.dev.platform_data = (void *)(smdev+1);
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
return &smdev->pdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_register_device
|
|
|
|
*
|
|
|
|
* Register a platform device created with sm501_create_subdev()
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int sm501_register_device(struct sm501_devdata *sm,
|
|
|
|
struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct sm501_device *smdev = to_sm_device(pdev);
|
|
|
|
int ptr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (ptr = 0; ptr < pdev->num_resources; ptr++) {
|
2010-11-13 04:37:56 +07:00
|
|
|
printk(KERN_DEBUG "%s[%d] %pR\n",
|
|
|
|
pdev->name, ptr, &pdev->resource[ptr]);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = platform_device_register(pdev);
|
|
|
|
|
|
|
|
if (ret >= 0) {
|
|
|
|
dev_dbg(sm->dev, "registered %s\n", pdev->name);
|
|
|
|
list_add_tail(&smdev->list, &sm->devices);
|
|
|
|
} else
|
|
|
|
dev_err(sm->dev, "error registering %s (%d)\n",
|
|
|
|
pdev->name, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_create_subio
|
|
|
|
*
|
|
|
|
* Fill in an IO resource for a sub device
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_create_subio(struct sm501_devdata *sm,
|
|
|
|
struct resource *res,
|
|
|
|
resource_size_t offs,
|
|
|
|
resource_size_t size)
|
|
|
|
{
|
|
|
|
res->flags = IORESOURCE_MEM;
|
|
|
|
res->parent = sm->io_res;
|
|
|
|
res->start = sm->io_res->start + offs;
|
|
|
|
res->end = res->start + size - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_create_mem
|
|
|
|
*
|
|
|
|
* Fill in an MEM resource for a sub device
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_create_mem(struct sm501_devdata *sm,
|
|
|
|
struct resource *res,
|
|
|
|
resource_size_t *offs,
|
|
|
|
resource_size_t size)
|
|
|
|
{
|
|
|
|
*offs -= size; /* adjust memory size */
|
|
|
|
|
|
|
|
res->flags = IORESOURCE_MEM;
|
|
|
|
res->parent = sm->mem_res;
|
|
|
|
res->start = sm->mem_res->start + *offs;
|
|
|
|
res->end = res->start + size - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_create_irq
|
|
|
|
*
|
|
|
|
* Fill in an IRQ resource for a sub device
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_create_irq(struct sm501_devdata *sm,
|
|
|
|
struct resource *res)
|
|
|
|
{
|
|
|
|
res->flags = IORESOURCE_IRQ;
|
|
|
|
res->parent = NULL;
|
|
|
|
res->start = res->end = sm->irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_register_usbhost(struct sm501_devdata *sm,
|
|
|
|
resource_size_t *mem_avail)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev;
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
pdev = sm501_create_subdev(sm, "sm501-usb", 3, 0);
|
2007-02-21 04:58:01 +07:00
|
|
|
if (!pdev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000);
|
|
|
|
sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024);
|
|
|
|
sm501_create_irq(sm, &pdev->resource[2]);
|
|
|
|
|
|
|
|
return sm501_register_device(sm, pdev);
|
|
|
|
}
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
static void sm501_setup_uart_data(struct sm501_devdata *sm,
|
|
|
|
struct plat_serial8250_port *uart_data,
|
|
|
|
unsigned int offset)
|
|
|
|
{
|
|
|
|
uart_data->membase = sm->regs + offset;
|
|
|
|
uart_data->mapbase = sm->io_res->start + offset;
|
|
|
|
uart_data->iotype = UPIO_MEM;
|
|
|
|
uart_data->irq = sm->irq;
|
|
|
|
uart_data->flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
|
|
|
|
uart_data->regshift = 2;
|
|
|
|
uart_data->uartclk = (9600 * 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_register_uart(struct sm501_devdata *sm, int devices)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev;
|
|
|
|
struct plat_serial8250_port *uart_data;
|
|
|
|
|
|
|
|
pdev = sm501_create_subdev(sm, "serial8250", 0,
|
|
|
|
sizeof(struct plat_serial8250_port) * 3);
|
|
|
|
if (!pdev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-07-30 15:10:05 +07:00
|
|
|
uart_data = dev_get_platdata(&pdev->dev);
|
2008-04-28 16:14:22 +07:00
|
|
|
|
|
|
|
if (devices & SM501_USE_UART0) {
|
|
|
|
sm501_setup_uart_data(sm, uart_data++, 0x30000);
|
|
|
|
sm501_unit_power(sm->dev, SM501_GATE_UART0, 1);
|
|
|
|
sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 12, 0);
|
|
|
|
sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x01e0, 0);
|
|
|
|
}
|
|
|
|
if (devices & SM501_USE_UART1) {
|
|
|
|
sm501_setup_uart_data(sm, uart_data++, 0x30020);
|
|
|
|
sm501_unit_power(sm->dev, SM501_GATE_UART1, 1);
|
|
|
|
sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 13, 0);
|
|
|
|
sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x1e00, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pdev->id = PLAT8250_DEV_SM501;
|
|
|
|
|
|
|
|
return sm501_register_device(sm, pdev);
|
|
|
|
}
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
static int sm501_register_display(struct sm501_devdata *sm,
|
|
|
|
resource_size_t *mem_avail)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev;
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
pdev = sm501_create_subdev(sm, "sm501-fb", 4, 0);
|
2007-02-21 04:58:01 +07:00
|
|
|
if (!pdev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000);
|
|
|
|
sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000);
|
|
|
|
sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail);
|
|
|
|
sm501_create_irq(sm, &pdev->resource[3]);
|
|
|
|
|
|
|
|
return sm501_register_device(sm, pdev);
|
|
|
|
}
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
#ifdef CONFIG_MFD_SM501_GPIO
|
|
|
|
|
|
|
|
static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio)
|
|
|
|
{
|
|
|
|
return container_of(gpio, struct sm501_devdata, gpio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|
|
|
|
|
|
|
{
|
2016-03-30 15:48:05 +07:00
|
|
|
struct sm501_gpio_chip *smgpio = gpiochip_get_data(chip);
|
2008-07-25 15:45:59 +07:00
|
|
|
unsigned long result;
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
|
2008-07-25 15:45:59 +07:00
|
|
|
result >>= offset;
|
|
|
|
|
|
|
|
return result & 1UL;
|
|
|
|
}
|
|
|
|
|
2009-01-10 22:59:53 +07:00
|
|
|
static void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip,
|
|
|
|
unsigned long bit)
|
|
|
|
{
|
|
|
|
unsigned long ctrl;
|
|
|
|
|
|
|
|
/* check and modify if this pin is not set as gpio. */
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
if (smc501_readl(smchip->control) & bit) {
|
2009-01-10 22:59:53 +07:00
|
|
|
dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev,
|
|
|
|
"changing mode of gpio, bit %08lx\n", bit);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
ctrl = smc501_readl(smchip->control);
|
2009-01-10 22:59:53 +07:00
|
|
|
ctrl &= ~bit;
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(ctrl, smchip->control);
|
2009-01-10 22:59:53 +07:00
|
|
|
|
|
|
|
sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|
|
|
|
|
|
|
{
|
2016-03-30 15:48:05 +07:00
|
|
|
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio *smgpio = smchip->ourgpio;
|
|
|
|
unsigned long bit = 1 << offset;
|
|
|
|
void __iomem *regs = smchip->regbase;
|
|
|
|
unsigned long save;
|
|
|
|
unsigned long val;
|
|
|
|
|
|
|
|
dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
|
|
|
|
__func__, chip, offset);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&smgpio->lock, save);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
|
2008-07-25 15:45:59 +07:00
|
|
|
if (value)
|
|
|
|
val |= bit;
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(val, regs);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
|
2009-01-10 22:59:53 +07:00
|
|
|
sm501_gpio_ensure_gpio(smchip, bit);
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
spin_unlock_irqrestore(&smgpio->lock, save);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
|
|
|
|
{
|
2016-03-30 15:48:05 +07:00
|
|
|
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio *smgpio = smchip->ourgpio;
|
|
|
|
void __iomem *regs = smchip->regbase;
|
|
|
|
unsigned long bit = 1 << offset;
|
|
|
|
unsigned long save;
|
|
|
|
unsigned long ddr;
|
|
|
|
|
2009-01-10 22:59:53 +07:00
|
|
|
dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
|
|
|
|
__func__, chip, offset);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&smgpio->lock, save);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
|
|
|
|
smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
|
2009-01-10 22:59:53 +07:00
|
|
|
sm501_gpio_ensure_gpio(smchip, bit);
|
|
|
|
|
2008-07-25 15:45:59 +07:00
|
|
|
spin_unlock_irqrestore(&smgpio->lock, save);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_gpio_output(struct gpio_chip *chip,
|
|
|
|
unsigned offset, int value)
|
|
|
|
{
|
2016-03-30 15:48:05 +07:00
|
|
|
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio *smgpio = smchip->ourgpio;
|
|
|
|
unsigned long bit = 1 << offset;
|
|
|
|
void __iomem *regs = smchip->regbase;
|
|
|
|
unsigned long save;
|
|
|
|
unsigned long val;
|
|
|
|
unsigned long ddr;
|
|
|
|
|
|
|
|
dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n",
|
|
|
|
__func__, chip, offset, value);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&smgpio->lock, save);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
val = smc501_readl(regs + SM501_GPIO_DATA_LOW);
|
2008-07-25 15:45:59 +07:00
|
|
|
if (value)
|
|
|
|
val |= bit;
|
|
|
|
else
|
|
|
|
val &= ~bit;
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(val, regs);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
|
|
|
|
smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(val, regs + SM501_GPIO_DATA_LOW);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
|
|
|
|
spin_unlock_irqrestore(&smgpio->lock, save);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-11 19:14:38 +07:00
|
|
|
static const struct gpio_chip gpio_chip_template = {
|
2008-07-25 15:45:59 +07:00
|
|
|
.ngpio = 32,
|
|
|
|
.direction_input = sm501_gpio_input,
|
|
|
|
.direction_output = sm501_gpio_output,
|
|
|
|
.set = sm501_gpio_set,
|
|
|
|
.get = sm501_gpio_get,
|
|
|
|
};
|
|
|
|
|
2012-11-20 01:23:04 +07:00
|
|
|
static int sm501_gpio_register_chip(struct sm501_devdata *sm,
|
2008-07-25 15:45:59 +07:00
|
|
|
struct sm501_gpio *gpio,
|
|
|
|
struct sm501_gpio_chip *chip)
|
|
|
|
{
|
|
|
|
struct sm501_platdata *pdata = sm->platdata;
|
|
|
|
struct gpio_chip *gchip = &chip->gpio;
|
2008-07-25 15:46:00 +07:00
|
|
|
int base = pdata->gpio_base;
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
chip->gpio = gpio_chip_template;
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
if (chip == &gpio->high) {
|
2008-07-25 15:46:00 +07:00
|
|
|
if (base > 0)
|
|
|
|
base += 32;
|
2008-07-25 15:45:59 +07:00
|
|
|
chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
|
2009-01-10 22:59:53 +07:00
|
|
|
chip->control = sm->regs + SM501_GPIO63_32_CONTROL;
|
2008-07-25 15:45:59 +07:00
|
|
|
gchip->label = "SM501-HIGH";
|
|
|
|
} else {
|
|
|
|
chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW;
|
2009-01-10 22:59:53 +07:00
|
|
|
chip->control = sm->regs + SM501_GPIO31_0_CONTROL;
|
2008-07-25 15:45:59 +07:00
|
|
|
gchip->label = "SM501-LOW";
|
|
|
|
}
|
|
|
|
|
|
|
|
gchip->base = base;
|
|
|
|
chip->ourgpio = gpio;
|
|
|
|
|
2016-03-30 15:48:05 +07:00
|
|
|
return gpiochip_add_data(gchip, chip);
|
2008-07-25 15:45:59 +07:00
|
|
|
}
|
|
|
|
|
2012-11-20 01:23:04 +07:00
|
|
|
static int sm501_register_gpio(struct sm501_devdata *sm)
|
2008-07-25 15:45:59 +07:00
|
|
|
{
|
|
|
|
struct sm501_gpio *gpio = &sm->gpio;
|
|
|
|
resource_size_t iobase = sm->io_res->start + SM501_GPIO;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dev_dbg(sm->dev, "registering gpio block %08llx\n",
|
|
|
|
(unsigned long long)iobase);
|
|
|
|
|
|
|
|
spin_lock_init(&gpio->lock);
|
|
|
|
|
|
|
|
gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio");
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!gpio->regs_res) {
|
2008-07-25 15:45:59 +07:00
|
|
|
dev_err(sm->dev, "gpio: failed to request region\n");
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
gpio->regs = ioremap(iobase, 0x20);
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!gpio->regs) {
|
2008-07-25 15:45:59 +07:00
|
|
|
dev_err(sm->dev, "gpio: failed to remap registers\n");
|
|
|
|
ret = -ENXIO;
|
2008-07-25 15:46:02 +07:00
|
|
|
goto err_claimed;
|
2008-07-25 15:45:59 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Register both our chips. */
|
|
|
|
|
|
|
|
ret = sm501_gpio_register_chip(sm, gpio, &gpio->low);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(sm->dev, "failed to add low chip\n");
|
|
|
|
goto err_mapped;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = sm501_gpio_register_chip(sm, gpio, &gpio->high);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(sm->dev, "failed to add high chip\n");
|
|
|
|
goto err_low_chip;
|
|
|
|
}
|
|
|
|
|
|
|
|
gpio->registered = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_low_chip:
|
2014-07-13 03:30:14 +07:00
|
|
|
gpiochip_remove(&gpio->low.gpio);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
|
|
|
err_mapped:
|
2008-07-25 15:46:02 +07:00
|
|
|
iounmap(gpio->regs);
|
|
|
|
|
|
|
|
err_claimed:
|
2008-07-25 15:45:59 +07:00
|
|
|
release_resource(gpio->regs_res);
|
|
|
|
kfree(gpio->regs_res);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sm501_gpio_remove(struct sm501_devdata *sm)
|
|
|
|
{
|
2008-07-25 15:46:02 +07:00
|
|
|
struct sm501_gpio *gpio = &sm->gpio;
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
if (!sm->gpio.registered)
|
|
|
|
return;
|
|
|
|
|
2014-07-13 03:30:14 +07:00
|
|
|
gpiochip_remove(&gpio->low.gpio);
|
|
|
|
gpiochip_remove(&gpio->high.gpio);
|
2008-07-25 15:46:02 +07:00
|
|
|
|
|
|
|
iounmap(gpio->regs);
|
|
|
|
release_resource(gpio->regs_res);
|
|
|
|
kfree(gpio->regs_res);
|
2008-07-25 15:45:59 +07:00
|
|
|
}
|
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
|
|
|
|
{
|
|
|
|
return sm->gpio.registered;
|
|
|
|
}
|
2008-07-25 15:45:59 +07:00
|
|
|
#else
|
2008-07-25 15:46:02 +07:00
|
|
|
static inline int sm501_register_gpio(struct sm501_devdata *sm)
|
2008-07-25 15:45:59 +07:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
static inline void sm501_gpio_remove(struct sm501_devdata *sm)
|
2008-07-25 15:45:59 +07:00
|
|
|
{
|
|
|
|
}
|
2008-07-25 15:46:01 +07:00
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-25 15:45:59 +07:00
|
|
|
#endif
|
|
|
|
|
2008-07-25 15:46:01 +07:00
|
|
|
static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
|
|
|
|
struct sm501_platdata_gpio_i2c *iic)
|
|
|
|
{
|
|
|
|
struct i2c_gpio_platform_data *icd;
|
|
|
|
struct platform_device *pdev;
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
struct gpiod_lookup_table *lookup;
|
2008-07-25 15:46:01 +07:00
|
|
|
|
|
|
|
pdev = sm501_create_subdev(sm, "i2c-gpio", 0,
|
|
|
|
sizeof(struct i2c_gpio_platform_data));
|
|
|
|
if (!pdev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
/* Create a gpiod lookup using gpiochip-local offsets */
|
|
|
|
lookup = devm_kzalloc(&pdev->dev,
|
|
|
|
sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup),
|
|
|
|
GFP_KERNEL);
|
|
|
|
lookup->dev_id = "i2c-gpio";
|
|
|
|
if (iic->pin_sda < 32)
|
|
|
|
lookup->table[0].chip_label = "SM501-LOW";
|
|
|
|
else
|
|
|
|
lookup->table[0].chip_label = "SM501-HIGH";
|
|
|
|
lookup->table[0].chip_hwnum = iic->pin_sda % 32;
|
|
|
|
lookup->table[0].con_id = NULL;
|
|
|
|
lookup->table[0].idx = 0;
|
2017-09-11 04:03:32 +07:00
|
|
|
lookup->table[0].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
if (iic->pin_scl < 32)
|
|
|
|
lookup->table[1].chip_label = "SM501-LOW";
|
|
|
|
else
|
|
|
|
lookup->table[1].chip_label = "SM501-HIGH";
|
|
|
|
lookup->table[1].chip_hwnum = iic->pin_scl % 32;
|
|
|
|
lookup->table[1].con_id = NULL;
|
|
|
|
lookup->table[1].idx = 1;
|
2017-09-11 04:03:32 +07:00
|
|
|
lookup->table[1].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN;
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
gpiod_add_lookup_table(lookup);
|
2008-07-25 15:46:01 +07:00
|
|
|
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
icd = dev_get_platdata(&pdev->dev);
|
2008-07-25 15:46:01 +07:00
|
|
|
icd->timeout = iic->timeout;
|
|
|
|
icd->udelay = iic->udelay;
|
|
|
|
|
|
|
|
/* note, we can't use either of the pin numbers, as the i2c-gpio
|
|
|
|
* driver uses the platform.id field to generate the bus number
|
|
|
|
* to register with the i2c core; The i2c core doesn't have enough
|
|
|
|
* entries to deal with anything we currently use.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pdev->id = iic->bus_num;
|
|
|
|
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
dev_info(sm->dev, "registering i2c-%d: sda=%d, scl=%d\n",
|
2008-07-25 15:46:01 +07:00
|
|
|
iic->bus_num,
|
i2c: gpio: Convert to use descriptors
This converts the GPIO-based I2C-driver to using GPIO
descriptors instead of the old global numberspace-based
GPIO interface. We:
- Convert the driver to unconditionally grab two GPIOs
from the device by index 0 (SDA) and 1 (SCL) which
will work fine with device tree and descriptor tables.
The existing device trees will continue to work just
like before, but without any roundtrip through the
global numberspace.
- Brutally convert all boardfiles still passing global
GPIOs by registering descriptor tables associated with
the devices instead so this driver does not need to keep
supporting passing any GPIO numbers as platform data.
There is no stepwise approach as elegant as this, I
strongly prefer this big hammer over any antsteps for this
conversion. This way the old GPIO numbers go away and
NEVER COME BACK.
Special conversion for the different boards utilizing
I2C-GPIO:
- EP93xx (arch/arm/mach-ep93xx): pretty straight forward as
all boards were using the same two GPIO lines, just define
these two in a lookup table for "i2c-gpio" and register
these along with the device. None of them define any
other platform data so just pass NULL as platform data.
This platform selects GPIOLIB so all should be smooth.
The pins appear on a gpiochip for bank "G" as pins 1 (SDA)
and 0 (SCL).
- IXP4 (arch/arm/mach-ixp4): descriptor tables have to
be registered for each board separately. They all use
"IXP4XX_GPIO_CHIP" so it is pretty straight forward.
Most board define no other platform data than SCL/SDA
so they can drop the #include of <linux/i2c-gpio.h> and
assign NULL to platform data.
The "goramo_mlr" (Goramo Multilink Router) board is a bit
worrisome: it implements its own I2C bit-banging in the
board file, and optionally registers an I2C serial port,
but claims the same GPIO lines for itself in the board file.
This is not going to work: there will be competition for the
GPIO lines, so delete the optional extra I2C bus instead, no
I2C devices are registered on it anyway, there are just hints
that it may contain an EEPROM that may be accessed from
userspace. This needs to be fixed up properly by the serial
clock using I2C emulation so drop a note in the code.
- KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c)
has some platform data in addition to the pins so it needs to
be kept around sans GPIO lines. Its GPIO chip is named
"KS8695" and the arch selects GPIOLIB.
- PXA boards (arch/arm/mach-pxa/*) use some of the platform
data so it needs to be preserved here. The viper board even
registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and
the arch selects GPIOLIB.
- SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO
I2C bus, and the arch selects GPIOLIB.
- Blackfin boards (arch/blackfin/bf533 etc) for these I assume
their I2C GPIOs refer to the local gpiochip defined in
arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO".
The arch selects GPIOLIB. The boards get spiked with
IF_ENABLED(I2C_GPIO) but that is a side effect of it
being like that already (I would just have Kconfig select
I2C_GPIO and get rid of them all.) I also delete any
platform data set to 0 as it will get that value anyway
from static declartions of platform data.
- The MIPS selects GPIOLIB and the Alchemy machine is using
two local GPIO chips, one of them has a GPIO I2C. We need
to adjust the local offset from the global number space here.
The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c
and AFAICT the chip is named "ath79-gpio" and the PB44
PCF857x expander spawns from this on GPIO 1 and 0. The latter
board only use the platform data to specify pins so it can be
cut altogether after this.
- The MFD Silicon Motion SM501 is a special case. It dynamically
spawns an I2C bus off the MFD using sm501_create_subdev().
We use an approach to dynamically create a machine descriptor
table and attach this to the "SM501-LOW" or "SM501-HIGH"
gpiochip. We use chip-local offsets to grab the right lines.
We can get rid of two local static inline helpers as part
of this refactoring.
Cc: Steven Miao <realmz6@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Heiko Schocher <hs@denx.de>
Acked-by: Wu, Aaron <Aaron.Wu@analog.com>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2017-09-10 06:30:46 +07:00
|
|
|
iic->pin_sda, iic->pin_scl);
|
2008-07-25 15:46:01 +07:00
|
|
|
|
|
|
|
return sm501_register_device(sm, pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
|
|
|
|
struct sm501_platdata *pdata)
|
|
|
|
{
|
|
|
|
struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c;
|
|
|
|
int index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) {
|
|
|
|
ret = sm501_register_gpio_i2c_instance(sm, iic);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
/* sm501_dbg_regs
|
|
|
|
*
|
|
|
|
* Debug attribute to attach to parent device to show core registers
|
|
|
|
*/
|
|
|
|
|
|
|
|
static ssize_t sm501_dbg_regs(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buff)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = dev_get_drvdata(dev) ;
|
|
|
|
unsigned int reg;
|
|
|
|
char *ptr = buff;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (reg = 0x00; reg < 0x70; reg += 4) {
|
|
|
|
ret = sprintf(ptr, "%08x = %08x\n",
|
2011-01-24 16:57:20 +07:00
|
|
|
reg, smc501_readl(sm->regs + reg));
|
2007-02-21 04:58:01 +07:00
|
|
|
ptr += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr - buff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-08 14:25:36 +07:00
|
|
|
static DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
/* sm501_init_reg
|
|
|
|
*
|
|
|
|
* Helper function for the init code to setup a register
|
2007-06-24 07:16:29 +07:00
|
|
|
*
|
|
|
|
* clear the bits which are set in r->mask, and then set
|
|
|
|
* the bits set in r->set.
|
2007-02-21 04:58:01 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
static inline void sm501_init_reg(struct sm501_devdata *sm,
|
|
|
|
unsigned long reg,
|
|
|
|
struct sm501_reg_init *r)
|
|
|
|
{
|
|
|
|
unsigned long tmp;
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
tmp = smc501_readl(sm->regs + reg);
|
2007-02-21 04:58:01 +07:00
|
|
|
tmp &= ~r->mask;
|
2007-06-24 07:16:29 +07:00
|
|
|
tmp |= r->set;
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(tmp, sm->regs + reg);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* sm501_init_regs
|
|
|
|
*
|
|
|
|
* Setup core register values
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void sm501_init_regs(struct sm501_devdata *sm,
|
|
|
|
struct sm501_initdata *init)
|
|
|
|
{
|
|
|
|
sm501_misc_control(sm->dev,
|
|
|
|
init->misc_control.set,
|
|
|
|
init->misc_control.mask);
|
|
|
|
|
|
|
|
sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing);
|
|
|
|
sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low);
|
|
|
|
sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high);
|
|
|
|
|
|
|
|
if (init->m1xclk) {
|
|
|
|
dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk);
|
|
|
|
sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk);
|
|
|
|
}
|
2007-06-24 07:16:29 +07:00
|
|
|
|
|
|
|
if (init->mclk) {
|
|
|
|
dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);
|
|
|
|
sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk);
|
|
|
|
}
|
2007-06-24 07:16:30 +07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the PLL sources for the M1CLK and M1XCLK
|
|
|
|
*
|
|
|
|
* If the M1CLK and M1XCLKs are not sourced from the same PLL, then
|
|
|
|
* there is a risk (see errata AB-5) that the SM501 will cease proper
|
|
|
|
* function. If this happens, then it is likely the SM501 will
|
|
|
|
* hang the system.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int sm501_check_clocks(struct sm501_devdata *sm)
|
|
|
|
{
|
2011-01-24 16:57:20 +07:00
|
|
|
unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
|
2007-06-24 07:16:30 +07:00
|
|
|
unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);
|
|
|
|
unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);
|
|
|
|
|
|
|
|
return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0));
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int sm501_mem_local[] = {
|
|
|
|
[0] = 4*1024*1024,
|
|
|
|
[1] = 8*1024*1024,
|
|
|
|
[2] = 16*1024*1024,
|
|
|
|
[3] = 32*1024*1024,
|
|
|
|
[4] = 64*1024*1024,
|
|
|
|
[5] = 2*1024*1024,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* sm501_init_dev
|
|
|
|
*
|
|
|
|
* Common init code for an SM501
|
|
|
|
*/
|
|
|
|
|
2012-11-20 01:23:04 +07:00
|
|
|
static int sm501_init_dev(struct sm501_devdata *sm)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
2008-04-28 16:14:22 +07:00
|
|
|
struct sm501_initdata *idata;
|
2008-07-25 15:46:01 +07:00
|
|
|
struct sm501_platdata *pdata;
|
2007-02-21 04:58:01 +07:00
|
|
|
resource_size_t mem_avail;
|
|
|
|
unsigned long dramctrl;
|
2007-06-24 07:16:31 +07:00
|
|
|
unsigned long devid;
|
2007-02-21 04:58:01 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_init(&sm->clock_lock);
|
|
|
|
spin_lock_init(&sm->reg_lock);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&sm->devices);
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
devid = smc501_readl(sm->regs + SM501_DEVICEID);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2007-06-24 07:16:31 +07:00
|
|
|
if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
|
|
|
|
dev_err(sm->dev, "incorrect device id %08lx\n", devid);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
/* disable irqs */
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(0, sm->regs + SM501_IRQ_MASK);
|
2008-04-28 16:14:22 +07:00
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL);
|
2007-02-21 04:58:01 +07:00
|
|
|
mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
|
|
|
|
|
2007-06-24 07:16:31 +07:00
|
|
|
dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
|
|
|
|
sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2008-03-05 05:28:50 +07:00
|
|
|
sm->rev = devid & SM501_DEVICEID_REVMASK;
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
sm501_dump_gate(sm);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
ret = device_create_file(sm->dev, &dev_attr_dbg_regs);
|
|
|
|
if (ret)
|
|
|
|
dev_err(sm->dev, "failed to create debug regs file\n");
|
|
|
|
|
|
|
|
sm501_dump_clk(sm);
|
|
|
|
|
|
|
|
/* check to see if we have some device initialisation */
|
|
|
|
|
2008-07-25 15:46:01 +07:00
|
|
|
pdata = sm->platdata;
|
|
|
|
idata = pdata ? pdata->init : NULL;
|
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
if (idata) {
|
|
|
|
sm501_init_regs(sm, idata);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2008-04-28 16:14:22 +07:00
|
|
|
if (idata->devices & SM501_USE_USB_HOST)
|
|
|
|
sm501_register_usbhost(sm, &mem_avail);
|
|
|
|
if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1))
|
|
|
|
sm501_register_uart(sm, idata->devices);
|
2008-07-25 15:45:59 +07:00
|
|
|
if (idata->devices & SM501_USE_GPIO)
|
|
|
|
sm501_register_gpio(sm);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
2018-03-08 20:20:06 +07:00
|
|
|
if (pdata && pdata->gpio_i2c && pdata->gpio_i2c_nr > 0) {
|
2008-07-25 15:46:02 +07:00
|
|
|
if (!sm501_gpio_isregistered(sm))
|
|
|
|
dev_err(sm->dev, "no gpio available for i2c gpio.\n");
|
2008-07-25 15:46:01 +07:00
|
|
|
else
|
|
|
|
sm501_register_gpio_i2c(sm, pdata);
|
|
|
|
}
|
|
|
|
|
2007-06-24 07:16:30 +07:00
|
|
|
ret = sm501_check_clocks(sm);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(sm->dev, "M1X and M clocks sourced from different "
|
|
|
|
"PLLs\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
/* always create a framebuffer */
|
|
|
|
sm501_register_display(sm, &mem_avail);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-20 01:23:04 +07:00
|
|
|
static int sm501_plat_probe(struct platform_device *dev)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
|
|
|
struct sm501_devdata *sm;
|
2008-10-22 06:39:55 +07:00
|
|
|
int ret;
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2018-03-08 20:05:41 +07:00
|
|
|
sm = kzalloc(sizeof(*sm), GFP_KERNEL);
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm) {
|
2008-10-22 06:39:55 +07:00
|
|
|
ret = -ENOMEM;
|
2007-02-21 04:58:01 +07:00
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sm->dev = &dev->dev;
|
|
|
|
sm->pdev_id = dev->id;
|
2013-07-30 15:10:05 +07:00
|
|
|
sm->platdata = dev_get_platdata(&dev->dev);
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2008-10-22 06:39:55 +07:00
|
|
|
ret = platform_get_irq(dev, 0);
|
|
|
|
if (ret < 0) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "failed to get irq resource\n");
|
|
|
|
goto err_res;
|
|
|
|
}
|
2008-10-22 06:39:55 +07:00
|
|
|
sm->irq = ret;
|
2007-02-21 04:58:01 +07:00
|
|
|
|
2008-10-22 06:39:55 +07:00
|
|
|
sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
|
|
|
|
sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm->io_res || !sm->mem_res) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "failed to get IO resource\n");
|
2008-10-22 06:39:55 +07:00
|
|
|
ret = -ENOENT;
|
2007-02-21 04:58:01 +07:00
|
|
|
goto err_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
sm->regs_claim = request_mem_region(sm->io_res->start,
|
2010-03-09 02:07:48 +07:00
|
|
|
0x100, "sm501");
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm->regs_claim) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "cannot claim registers\n");
|
2008-10-22 06:39:55 +07:00
|
|
|
ret = -EBUSY;
|
2007-02-21 04:58:01 +07:00
|
|
|
goto err_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
platform_set_drvdata(dev, sm);
|
|
|
|
|
2010-01-06 01:55:54 +07:00
|
|
|
sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res));
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm->regs) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "cannot remap registers\n");
|
2008-10-22 06:39:55 +07:00
|
|
|
ret = -EIO;
|
2007-02-21 04:58:01 +07:00
|
|
|
goto err_claim;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sm501_init_dev(sm);
|
|
|
|
|
|
|
|
err_claim:
|
|
|
|
release_resource(sm->regs_claim);
|
|
|
|
kfree(sm->regs_claim);
|
|
|
|
err_res:
|
|
|
|
kfree(sm);
|
|
|
|
err1:
|
2008-10-22 06:39:55 +07:00
|
|
|
return ret;
|
2007-02-21 04:58:01 +07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
#ifdef CONFIG_PM
|
2008-07-25 15:45:58 +07:00
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
/* power management support */
|
|
|
|
|
2008-07-25 15:45:58 +07:00
|
|
|
static void sm501_set_power(struct sm501_devdata *sm, int on)
|
|
|
|
{
|
|
|
|
struct sm501_platdata *pd = sm->platdata;
|
|
|
|
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!pd)
|
2008-07-25 15:45:58 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (pd->get_power) {
|
|
|
|
if (pd->get_power(sm->dev) == on) {
|
|
|
|
dev_dbg(sm->dev, "is already %d\n", on);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pd->set_power) {
|
|
|
|
dev_dbg(sm->dev, "setting power to %d\n", on);
|
|
|
|
|
|
|
|
pd->set_power(sm->dev, on);
|
|
|
|
sm501_mdelay(sm, 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
sm->in_suspend = 1;
|
2011-01-24 16:57:20 +07:00
|
|
|
sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
|
2007-06-24 07:16:28 +07:00
|
|
|
|
|
|
|
sm501_dump_regs(sm);
|
2008-07-25 15:45:58 +07:00
|
|
|
|
|
|
|
if (sm->platdata) {
|
|
|
|
if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF)
|
|
|
|
sm501_set_power(sm, 0);
|
|
|
|
}
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_plat_resume(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = platform_get_drvdata(pdev);
|
|
|
|
|
2008-07-25 15:45:58 +07:00
|
|
|
sm501_set_power(sm, 1);
|
|
|
|
|
2007-06-24 07:16:28 +07:00
|
|
|
sm501_dump_regs(sm);
|
|
|
|
sm501_dump_gate(sm);
|
|
|
|
sm501_dump_clk(sm);
|
|
|
|
|
|
|
|
/* check to see if we are in the same state as when suspended */
|
|
|
|
|
2011-01-24 16:57:20 +07:00
|
|
|
if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {
|
2007-06-24 07:16:28 +07:00
|
|
|
dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n");
|
2011-01-24 16:57:20 +07:00
|
|
|
smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);
|
2007-06-24 07:16:28 +07:00
|
|
|
|
|
|
|
/* our suspend causes the controller state to change,
|
|
|
|
* either by something attempting setup, power loss,
|
|
|
|
* or an external reset event on power change */
|
|
|
|
|
|
|
|
if (sm->platdata && sm->platdata->init) {
|
|
|
|
sm501_init_regs(sm, sm->platdata->init);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dump our state from resume */
|
|
|
|
|
|
|
|
sm501_dump_regs(sm);
|
|
|
|
sm501_dump_clk(sm);
|
|
|
|
|
|
|
|
sm->in_suspend = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define sm501_plat_suspend NULL
|
|
|
|
#define sm501_plat_resume NULL
|
|
|
|
#endif
|
|
|
|
|
2007-02-21 04:58:01 +07:00
|
|
|
/* Initialisation data for PCI devices */
|
|
|
|
|
|
|
|
static struct sm501_initdata sm501_pci_initdata = {
|
|
|
|
.gpio_high = {
|
|
|
|
.set = 0x3F000000, /* 24bit panel */
|
|
|
|
.mask = 0x0,
|
|
|
|
},
|
|
|
|
.misc_timing = {
|
|
|
|
.set = 0x010100, /* SDRAM timing */
|
|
|
|
.mask = 0x1F1F00,
|
|
|
|
},
|
|
|
|
.misc_control = {
|
|
|
|
.set = SM501_MISC_PNL_24BIT,
|
|
|
|
.mask = 0,
|
|
|
|
},
|
|
|
|
|
|
|
|
.devices = SM501_USE_ALL,
|
2007-06-24 07:16:30 +07:00
|
|
|
|
|
|
|
/* Errata AB-3 says that 72MHz is the fastest available
|
|
|
|
* for 33MHZ PCI with proper bus-mastering operation */
|
|
|
|
|
|
|
|
.mclk = 72 * MHZ,
|
|
|
|
.m1xclk = 144 * MHZ,
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
|
|
|
|
.flags = (SM501FB_FLAG_USE_INIT_MODE |
|
|
|
|
SM501FB_FLAG_USE_HWCURSOR |
|
|
|
|
SM501FB_FLAG_USE_HWACCEL |
|
|
|
|
SM501FB_FLAG_DISABLE_AT_EXIT),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sm501_platdata_fb sm501_fb_pdata = {
|
|
|
|
.fb_route = SM501_FB_OWN,
|
|
|
|
.fb_crt = &sm501_pdata_fbsub,
|
|
|
|
.fb_pnl = &sm501_pdata_fbsub,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sm501_platdata sm501_pci_platdata = {
|
|
|
|
.init = &sm501_pci_initdata,
|
|
|
|
.fb = &sm501_fb_pdata,
|
2008-07-25 15:46:00 +07:00
|
|
|
.gpio_base = -1,
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
2012-11-20 01:23:04 +07:00
|
|
|
static int sm501_pci_probe(struct pci_dev *dev,
|
2009-02-17 15:10:19 +07:00
|
|
|
const struct pci_device_id *id)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
|
|
|
struct sm501_devdata *sm;
|
|
|
|
int err;
|
|
|
|
|
2018-03-08 20:05:41 +07:00
|
|
|
sm = kzalloc(sizeof(*sm), GFP_KERNEL);
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm) {
|
2007-02-21 04:58:01 +07:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set a default set of platform data */
|
|
|
|
dev->dev.platform_data = sm->platdata = &sm501_pci_platdata;
|
|
|
|
|
|
|
|
/* set a hopefully unique id for our child platform devices */
|
|
|
|
sm->pdev_id = 32 + dev->devfn;
|
|
|
|
|
|
|
|
pci_set_drvdata(dev, sm);
|
|
|
|
|
|
|
|
err = pci_enable_device(dev);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&dev->dev, "cannot enable device\n");
|
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
|
|
|
sm->dev = &dev->dev;
|
|
|
|
sm->irq = dev->irq;
|
|
|
|
|
|
|
|
#ifdef __BIG_ENDIAN
|
|
|
|
/* if the system is big-endian, we most probably have a
|
|
|
|
* translation in the IO layer making the PCI bus little endian
|
|
|
|
* so make the framebuffer swapped pixels */
|
|
|
|
|
|
|
|
sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* check our resources */
|
|
|
|
|
|
|
|
if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) {
|
|
|
|
dev_err(&dev->dev, "region #0 is not memory?\n");
|
|
|
|
err = -EINVAL;
|
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) {
|
|
|
|
dev_err(&dev->dev, "region #1 is not memory?\n");
|
|
|
|
err = -EINVAL;
|
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make our resources ready for sharing */
|
|
|
|
|
|
|
|
sm->io_res = &dev->resource[1];
|
|
|
|
sm->mem_res = &dev->resource[0];
|
|
|
|
|
|
|
|
sm->regs_claim = request_mem_region(sm->io_res->start,
|
2010-03-09 02:07:48 +07:00
|
|
|
0x100, "sm501");
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm->regs_claim) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "cannot claim registers\n");
|
|
|
|
err= -EBUSY;
|
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
2008-09-29 06:14:52 +07:00
|
|
|
sm->regs = pci_ioremap_bar(dev, 1);
|
2018-03-08 20:20:06 +07:00
|
|
|
if (!sm->regs) {
|
2007-02-21 04:58:01 +07:00
|
|
|
dev_err(&dev->dev, "cannot remap registers\n");
|
|
|
|
err = -EIO;
|
|
|
|
goto err4;
|
|
|
|
}
|
|
|
|
|
|
|
|
sm501_init_dev(sm);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err4:
|
|
|
|
release_resource(sm->regs_claim);
|
|
|
|
kfree(sm->regs_claim);
|
|
|
|
err3:
|
|
|
|
pci_disable_device(dev);
|
|
|
|
err2:
|
|
|
|
kfree(sm);
|
|
|
|
err1:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sm501_remove_sub(struct sm501_devdata *sm,
|
|
|
|
struct sm501_device *smdev)
|
|
|
|
{
|
|
|
|
list_del(&smdev->list);
|
|
|
|
platform_device_unregister(&smdev->pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sm501_dev_remove(struct sm501_devdata *sm)
|
|
|
|
{
|
|
|
|
struct sm501_device *smdev, *tmp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(smdev, tmp, &sm->devices, list)
|
|
|
|
sm501_remove_sub(sm, smdev);
|
|
|
|
|
|
|
|
device_remove_file(sm->dev, &dev_attr_dbg_regs);
|
2008-07-25 15:45:59 +07:00
|
|
|
|
2008-07-25 15:46:02 +07:00
|
|
|
sm501_gpio_remove(sm);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
2012-11-20 01:26:01 +07:00
|
|
|
static void sm501_pci_remove(struct pci_dev *dev)
|
2007-02-21 04:58:01 +07:00
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = pci_get_drvdata(dev);
|
|
|
|
|
|
|
|
sm501_dev_remove(sm);
|
|
|
|
iounmap(sm->regs);
|
|
|
|
|
|
|
|
release_resource(sm->regs_claim);
|
|
|
|
kfree(sm->regs_claim);
|
|
|
|
|
|
|
|
pci_disable_device(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sm501_plat_remove(struct platform_device *dev)
|
|
|
|
{
|
|
|
|
struct sm501_devdata *sm = platform_get_drvdata(dev);
|
|
|
|
|
|
|
|
sm501_dev_remove(sm);
|
|
|
|
iounmap(sm->regs);
|
|
|
|
|
|
|
|
release_resource(sm->regs_claim);
|
|
|
|
kfree(sm->regs_claim);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-03 06:15:39 +07:00
|
|
|
static const struct pci_device_id sm501_pci_tbl[] = {
|
2007-02-21 04:58:01 +07:00
|
|
|
{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
|
|
|
{ 0, },
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, sm501_pci_tbl);
|
|
|
|
|
2009-02-17 15:10:19 +07:00
|
|
|
static struct pci_driver sm501_pci_driver = {
|
2007-02-21 04:58:01 +07:00
|
|
|
.name = "sm501",
|
|
|
|
.id_table = sm501_pci_tbl,
|
|
|
|
.probe = sm501_pci_probe,
|
2012-11-20 01:20:24 +07:00
|
|
|
.remove = sm501_pci_remove,
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
2008-07-25 15:45:47 +07:00
|
|
|
MODULE_ALIAS("platform:sm501");
|
|
|
|
|
2014-05-07 15:24:05 +07:00
|
|
|
static const struct of_device_id of_sm501_match_tbl[] = {
|
2011-01-26 14:21:30 +07:00
|
|
|
{ .compatible = "smi,sm501", },
|
|
|
|
{ /* end */ }
|
|
|
|
};
|
2015-09-18 01:16:50 +07:00
|
|
|
MODULE_DEVICE_TABLE(of, of_sm501_match_tbl);
|
2011-01-26 14:21:30 +07:00
|
|
|
|
2009-02-17 15:10:19 +07:00
|
|
|
static struct platform_driver sm501_plat_driver = {
|
2007-02-21 04:58:01 +07:00
|
|
|
.driver = {
|
|
|
|
.name = "sm501",
|
2011-01-26 14:21:30 +07:00
|
|
|
.of_match_table = of_sm501_match_tbl,
|
2007-02-21 04:58:01 +07:00
|
|
|
},
|
|
|
|
.probe = sm501_plat_probe,
|
|
|
|
.remove = sm501_plat_remove,
|
2007-06-24 07:16:28 +07:00
|
|
|
.suspend = sm501_plat_suspend,
|
|
|
|
.resume = sm501_plat_resume,
|
2007-02-21 04:58:01 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init sm501_base_init(void)
|
|
|
|
{
|
2009-02-17 15:10:19 +07:00
|
|
|
platform_driver_register(&sm501_plat_driver);
|
|
|
|
return pci_register_driver(&sm501_pci_driver);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit sm501_base_exit(void)
|
|
|
|
{
|
2009-02-17 15:10:19 +07:00
|
|
|
platform_driver_unregister(&sm501_plat_driver);
|
|
|
|
pci_unregister_driver(&sm501_pci_driver);
|
2007-02-21 04:58:01 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(sm501_base_init);
|
|
|
|
module_exit(sm501_base_exit);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("SM501 Core Driver");
|
|
|
|
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders");
|
|
|
|
MODULE_LICENSE("GPL v2");
|