linux-stable/drivers/mfd/sm501.c
Linus Walleij b2e6355559 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-10-30 08:42:21 +01:00

1752 lines
41 KiB
C

/* 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>
#include <linux/i2c-gpio.h>
#include <linux/gpio/machine.h>
#include <linux/slab.h>
#include <linux/sm501.h>
#include <linux/sm501-regs.h>
#include <linux/serial_8250.h>
#include <linux/io.h>
struct sm501_device {
struct list_head list;
struct platform_device pdev;
};
struct sm501_gpio;
#ifdef CONFIG_MFD_SM501_GPIO
#include <linux/gpio.h>
struct sm501_gpio_chip {
struct gpio_chip gpio;
struct sm501_gpio *ourgpio; /* to get back to parent. */
void __iomem *regbase;
void __iomem *control; /* address of control reg. */
};
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;
};
#else
struct sm501_gpio {
/* no gpio support, empty definition for sm501_devdata. */
};
#endif
struct sm501_devdata {
spinlock_t reg_lock;
struct mutex clock_lock;
struct list_head devices;
struct sm501_gpio gpio;
struct device *dev;
struct resource *io_res;
struct resource *mem_res;
struct resource *regs_claim;
struct sm501_platdata *platdata;
unsigned int in_suspend;
unsigned long pm_misc;
int unit_power[20];
unsigned int pdev_id;
unsigned int irq;
void __iomem *regs;
unsigned int rev;
};
#define MHZ (1000 * 1000)
#ifdef DEBUG
static const unsigned int div_tab[] = {
[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,
unsigned long mask)
{
if (val & selbit)
pll2 = 288 * MHZ;
return pll2 / div_tab[(val >> lshft) & mask];
}
#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)
{
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);
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;
sdclk0 /= div_tab[((misct >> 8) & 0xf)];
sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ;
sdclk1 /= div_tab[((misct >> 16) & 0xf)];
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), "
"M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
(pmc & 3 ) == 0 ? '*' : '-',
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)));
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 ? '*' : '-',
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)));
}
static void sm501_dump_regs(struct sm501_devdata *sm)
{
void __iomem *regs = sm->regs;
dev_info(sm->dev, "System Control %08x\n",
smc501_readl(regs + SM501_SYSTEM_CONTROL));
dev_info(sm->dev, "Misc Control %08x\n",
smc501_readl(regs + SM501_MISC_CONTROL));
dev_info(sm->dev, "GPIO Control Low %08x\n",
smc501_readl(regs + SM501_GPIO31_0_CONTROL));
dev_info(sm->dev, "GPIO Control Hi %08x\n",
smc501_readl(regs + SM501_GPIO63_32_CONTROL));
dev_info(sm->dev, "DRAM Control %08x\n",
smc501_readl(regs + SM501_DRAM_CONTROL));
dev_info(sm->dev, "Arbitration Ctrl %08x\n",
smc501_readl(regs + SM501_ARBTRTN_CONTROL));
dev_info(sm->dev, "Misc Timing %08x\n",
smc501_readl(regs + SM501_MISC_TIMING));
}
static void sm501_dump_gate(struct sm501_devdata *sm)
{
dev_info(sm->dev, "CurrentGate %08x\n",
smc501_readl(sm->regs + SM501_CURRENT_GATE));
dev_info(sm->dev, "CurrentClock %08x\n",
smc501_readl(sm->regs + SM501_CURRENT_CLOCK));
dev_info(sm->dev, "PowerModeControl %08x\n",
smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL));
}
#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) { }
#endif
/* sm501_sync_regs
*
* ensure the
*/
static void sm501_sync_regs(struct sm501_devdata *sm)
{
smc501_readl(sm->regs);
}
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);
}
/* sm501_misc_control
*
* alters the miscellaneous control parameters
*/
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);
misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
to = (misc & ~clear) | set;
if (to != misc) {
smc501_writel(to, sm->regs + SM501_MISC_CONTROL);
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);
data = smc501_readl(sm->regs + reg);
data |= set;
data &= ~clear;
smc501_writel(data, sm->regs + reg);
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);
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);
mode &= 3; /* get current power mode */
if (unit >= ARRAY_SIZE(sm->unit_power)) {
dev_err(dev, "%s: bad unit %d\n", __func__, unit);
goto already;
}
dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit,
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:
smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
mode = 0;
break;
case 2:
case 0:
smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
mode = 1;
break;
default:
gate = -1;
goto already;
}
smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
sm501_sync_regs(sm);
dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
gate, clock, mode);
sm501_mdelay(sm, 16);
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;
unsigned int m, n, k;
};
/* 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 */
diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq;
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);
}
/* sm501_select_clock
*
* Calculates the nearest discrete clock frequency that can be
* achieved using the 288MHz and 336MHz PLLs.
* the maximum divisor is 3 or 5
*/
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) {
sm501_calc_clock(freq, clock, max_div, mclk, &best_diff);
}
/* 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);
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);
unsigned int pll_reg = 0;
unsigned long sm501_freq; /* the actual frequency achieved */
u64 reg;
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:
/* This clock is divided in half so to achieve the
* requested frequency the value must be multiplied by
* 2. This clock also has an additional pre divisor */
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 */
}
break;
case SM501_CLOCK_V2XCLK:
/* This clock is divided in half so to achieve the
* 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);
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);
clock = clock & ~(0xFF << clksrc);
clock |= reg<<clksrc;
mode &= 3; /* find current mode */
switch (mode) {
case 1:
smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
mode = 0;
break;
case 2:
case 0:
smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
mode = 1;
break;
default:
mutex_unlock(&sm->clock_lock);
return -1;
}
smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
if (pll_reg)
smc501_writel(pll_reg,
sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL);
sm501_sync_regs(sm);
dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
gate, clock, mode);
sm501_mdelay(sm, 16);
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
*/
unsigned long sm501_find_clock(struct device *dev,
int clksrc,
unsigned long req_freq)
{
struct sm501_devdata *sm = dev_get_drvdata(dev);
unsigned long sm501_freq; /* the frequency achieveable by the 501 */
struct sm501_clock to;
switch (clksrc) {
case SM501_CLOCK_P2XCLK:
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);
}
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 *
sm501_create_subdev(struct sm501_devdata *sm, char *name,
unsigned int res_count, unsigned int platform_data_size)
{
struct sm501_device *smdev;
smdev = kzalloc(sizeof(struct sm501_device) +
(sizeof(struct resource) * res_count) +
platform_data_size, GFP_KERNEL);
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;
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);
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++) {
printk(KERN_DEBUG "%s[%d] %pR\n",
pdev->name, ptr, &pdev->resource[ptr]);
}
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;
pdev = sm501_create_subdev(sm, "sm501-usb", 3, 0);
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);
}
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;
uart_data = dev_get_platdata(&pdev->dev);
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);
}
static int sm501_register_display(struct sm501_devdata *sm,
resource_size_t *mem_avail)
{
struct platform_device *pdev;
pdev = sm501_create_subdev(sm, "sm501-fb", 4, 0);
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);
}
#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)
{
struct sm501_gpio_chip *smgpio = gpiochip_get_data(chip);
unsigned long result;
result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
result >>= offset;
return result & 1UL;
}
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. */
if (smc501_readl(smchip->control) & bit) {
dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev,
"changing mode of gpio, bit %08lx\n", bit);
ctrl = smc501_readl(smchip->control);
ctrl &= ~bit;
smc501_writel(ctrl, smchip->control);
sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio));
}
}
static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
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);
val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
if (value)
val |= bit;
smc501_writel(val, regs);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
sm501_gpio_ensure_gpio(smchip, bit);
spin_unlock_irqrestore(&smgpio->lock, save);
}
static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
struct sm501_gpio *smgpio = smchip->ourgpio;
void __iomem *regs = smchip->regbase;
unsigned long bit = 1 << offset;
unsigned long save;
unsigned long ddr;
dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
__func__, chip, offset);
spin_lock_irqsave(&smgpio->lock, save);
ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
sm501_gpio_ensure_gpio(smchip, bit);
spin_unlock_irqrestore(&smgpio->lock, save);
return 0;
}
static int sm501_gpio_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
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);
val = smc501_readl(regs + SM501_GPIO_DATA_LOW);
if (value)
val |= bit;
else
val &= ~bit;
smc501_writel(val, regs);
ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
smc501_writel(val, regs + SM501_GPIO_DATA_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
spin_unlock_irqrestore(&smgpio->lock, save);
return 0;
}
static const struct gpio_chip gpio_chip_template = {
.ngpio = 32,
.direction_input = sm501_gpio_input,
.direction_output = sm501_gpio_output,
.set = sm501_gpio_set,
.get = sm501_gpio_get,
};
static int sm501_gpio_register_chip(struct sm501_devdata *sm,
struct sm501_gpio *gpio,
struct sm501_gpio_chip *chip)
{
struct sm501_platdata *pdata = sm->platdata;
struct gpio_chip *gchip = &chip->gpio;
int base = pdata->gpio_base;
chip->gpio = gpio_chip_template;
if (chip == &gpio->high) {
if (base > 0)
base += 32;
chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
chip->control = sm->regs + SM501_GPIO63_32_CONTROL;
gchip->label = "SM501-HIGH";
} else {
chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW;
chip->control = sm->regs + SM501_GPIO31_0_CONTROL;
gchip->label = "SM501-LOW";
}
gchip->base = base;
chip->ourgpio = gpio;
return gpiochip_add_data(gchip, chip);
}
static int sm501_register_gpio(struct sm501_devdata *sm)
{
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");
if (gpio->regs_res == NULL) {
dev_err(sm->dev, "gpio: failed to request region\n");
return -ENXIO;
}
gpio->regs = ioremap(iobase, 0x20);
if (gpio->regs == NULL) {
dev_err(sm->dev, "gpio: failed to remap registers\n");
ret = -ENXIO;
goto err_claimed;
}
/* 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:
gpiochip_remove(&gpio->low.gpio);
err_mapped:
iounmap(gpio->regs);
err_claimed:
release_resource(gpio->regs_res);
kfree(gpio->regs_res);
return ret;
}
static void sm501_gpio_remove(struct sm501_devdata *sm)
{
struct sm501_gpio *gpio = &sm->gpio;
if (!sm->gpio.registered)
return;
gpiochip_remove(&gpio->low.gpio);
gpiochip_remove(&gpio->high.gpio);
iounmap(gpio->regs);
release_resource(gpio->regs_res);
kfree(gpio->regs_res);
}
static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
{
return sm->gpio.registered;
}
#else
static inline int sm501_register_gpio(struct sm501_devdata *sm)
{
return 0;
}
static inline void sm501_gpio_remove(struct sm501_devdata *sm)
{
}
static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
{
return 0;
}
#endif
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;
struct gpiod_lookup_table *lookup;
pdev = sm501_create_subdev(sm, "i2c-gpio", 0,
sizeof(struct i2c_gpio_platform_data));
if (!pdev)
return -ENOMEM;
/* 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;
lookup->table[0].flags = GPIO_ACTIVE_HIGH;
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;
lookup->table[1].flags = GPIO_ACTIVE_HIGH;
gpiod_add_lookup_table(lookup);
icd = dev_get_platdata(&pdev->dev);
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;
dev_info(sm->dev, "registering i2c-%d: sda=%d, scl=%d\n",
iic->bus_num,
iic->pin_sda, iic->pin_scl);
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;
}
/* 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",
reg, smc501_readl(sm->regs + reg));
ptr += ret;
}
return ptr - buff;
}
static DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL);
/* sm501_init_reg
*
* Helper function for the init code to setup a register
*
* clear the bits which are set in r->mask, and then set
* the bits set in r->set.
*/
static inline void sm501_init_reg(struct sm501_devdata *sm,
unsigned long reg,
struct sm501_reg_init *r)
{
unsigned long tmp;
tmp = smc501_readl(sm->regs + reg);
tmp &= ~r->mask;
tmp |= r->set;
smc501_writel(tmp, sm->regs + reg);
}
/* 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);
}
if (init->mclk) {
dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);
sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk);
}
}
/* 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)
{
unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
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));
}
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
*/
static int sm501_init_dev(struct sm501_devdata *sm)
{
struct sm501_initdata *idata;
struct sm501_platdata *pdata;
resource_size_t mem_avail;
unsigned long dramctrl;
unsigned long devid;
int ret;
mutex_init(&sm->clock_lock);
spin_lock_init(&sm->reg_lock);
INIT_LIST_HEAD(&sm->devices);
devid = smc501_readl(sm->regs + SM501_DEVICEID);
if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
dev_err(sm->dev, "incorrect device id %08lx\n", devid);
return -EINVAL;
}
/* disable irqs */
smc501_writel(0, sm->regs + SM501_IRQ_MASK);
dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL);
mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
sm->rev = devid & SM501_DEVICEID_REVMASK;
sm501_dump_gate(sm);
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 */
pdata = sm->platdata;
idata = pdata ? pdata->init : NULL;
if (idata) {
sm501_init_regs(sm, idata);
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);
if (idata->devices & SM501_USE_GPIO)
sm501_register_gpio(sm);
}
if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
if (!sm501_gpio_isregistered(sm))
dev_err(sm->dev, "no gpio available for i2c gpio.\n");
else
sm501_register_gpio_i2c(sm, pdata);
}
ret = sm501_check_clocks(sm);
if (ret) {
dev_err(sm->dev, "M1X and M clocks sourced from different "
"PLLs\n");
return -EINVAL;
}
/* always create a framebuffer */
sm501_register_display(sm, &mem_avail);
return 0;
}
static int sm501_plat_probe(struct platform_device *dev)
{
struct sm501_devdata *sm;
int ret;
sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);
if (sm == NULL) {
dev_err(&dev->dev, "no memory for device data\n");
ret = -ENOMEM;
goto err1;
}
sm->dev = &dev->dev;
sm->pdev_id = dev->id;
sm->platdata = dev_get_platdata(&dev->dev);
ret = platform_get_irq(dev, 0);
if (ret < 0) {
dev_err(&dev->dev, "failed to get irq resource\n");
goto err_res;
}
sm->irq = ret;
sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (sm->io_res == NULL || sm->mem_res == NULL) {
dev_err(&dev->dev, "failed to get IO resource\n");
ret = -ENOENT;
goto err_res;
}
sm->regs_claim = request_mem_region(sm->io_res->start,
0x100, "sm501");
if (sm->regs_claim == NULL) {
dev_err(&dev->dev, "cannot claim registers\n");
ret = -EBUSY;
goto err_res;
}
platform_set_drvdata(dev, sm);
sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res));
if (sm->regs == NULL) {
dev_err(&dev->dev, "cannot remap registers\n");
ret = -EIO;
goto err_claim;
}
return sm501_init_dev(sm);
err_claim:
release_resource(sm->regs_claim);
kfree(sm->regs_claim);
err_res:
kfree(sm);
err1:
return ret;
}
#ifdef CONFIG_PM
/* power management support */
static void sm501_set_power(struct sm501_devdata *sm, int on)
{
struct sm501_platdata *pd = sm->platdata;
if (pd == NULL)
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);
}
}
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;
sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
sm501_dump_regs(sm);
if (sm->platdata) {
if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF)
sm501_set_power(sm, 0);
}
return 0;
}
static int sm501_plat_resume(struct platform_device *pdev)
{
struct sm501_devdata *sm = platform_get_drvdata(pdev);
sm501_set_power(sm, 1);
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 */
if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {
dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n");
smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);
/* 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
/* 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,
/* Errata AB-3 says that 72MHz is the fastest available
* for 33MHZ PCI with proper bus-mastering operation */
.mclk = 72 * MHZ,
.m1xclk = 144 * MHZ,
};
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,
.gpio_base = -1,
};
static int sm501_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct sm501_devdata *sm;
int err;
sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);
if (sm == NULL) {
dev_err(&dev->dev, "no memory for device data\n");
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,
0x100, "sm501");
if (sm->regs_claim == NULL) {
dev_err(&dev->dev, "cannot claim registers\n");
err= -EBUSY;
goto err3;
}
sm->regs = pci_ioremap_bar(dev, 1);
if (sm->regs == NULL) {
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);
sm501_gpio_remove(sm);
}
static void sm501_pci_remove(struct pci_dev *dev)
{
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;
}
static const struct pci_device_id sm501_pci_tbl[] = {
{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, sm501_pci_tbl);
static struct pci_driver sm501_pci_driver = {
.name = "sm501",
.id_table = sm501_pci_tbl,
.probe = sm501_pci_probe,
.remove = sm501_pci_remove,
};
MODULE_ALIAS("platform:sm501");
static const struct of_device_id of_sm501_match_tbl[] = {
{ .compatible = "smi,sm501", },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, of_sm501_match_tbl);
static struct platform_driver sm501_plat_driver = {
.driver = {
.name = "sm501",
.of_match_table = of_sm501_match_tbl,
},
.probe = sm501_plat_probe,
.remove = sm501_plat_remove,
.suspend = sm501_plat_suspend,
.resume = sm501_plat_resume,
};
static int __init sm501_base_init(void)
{
platform_driver_register(&sm501_plat_driver);
return pci_register_driver(&sm501_pci_driver);
}
static void __exit sm501_base_exit(void)
{
platform_driver_unregister(&sm501_plat_driver);
pci_unregister_driver(&sm501_pci_driver);
}
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");