GPMC (General Purpose Memory Controller) changes for omaps. These

changes allow us to drop dependencies to bootloader timings now
 that the known device tree entries have been fixed. So we can now
 require proper timings to be configured and get rid of the legacy
 smsc91x code.
 
 Note that this branch has a dependency to the related device tree
 branch sent in a separate pull request as timings are now required.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUY5toAAoJEBvUPslcq6VzHAwQALbDoi++oqPFfgd9iHZngsFo
 wxAsb1n4ENaazBJtG2SayDDUo7h3zxKrh/NRqEO43g7oW1V6nD86a5ItM7HlnzH6
 upjHAb8O3du3SL8Zmu+AMgcoqB2yz8iZjphPW2r25gnt6on9uFQio5AVMturEpO0
 PVyWdpT0sTvLa1JZRjhmn/2cLC1z7eFdG4yjQQdwbrR5WDUX3IQz3X6n7dTM5IJy
 oH+gM2k9x8ymR7rfFDjBEuZl+0ShHJLfg30ubAGai+MfyLtSBSdKIIpiNdVEbbCe
 ytMVhTD4pS+5ToVNZ8m5rGtZjY+g0VN57wM8AfKTN7OF6L0nb8ZeOoz0dEJkXQcw
 IlZ0MP1/k/HhOyQ8FRWcSqvG/frvQjQSHNTaoJAWNvjpHtxQ4Lox1r5mnmy3PICv
 2SxDYEysohwBrv/TQJlTMDJn+ndAvygbGxDbMYMbpqwmC1jsL5PA4vMnpMVDrBWo
 h7V2dHd/uw9uFB+FnSKvOJYMYs2vdsArlxYrk8br0RHt7syugWiVfKDjPvBhOum2
 y24Q6bRXUZqloiq2hG4VNN+vhmB6TNdnAXV15kzp+pzmsNh3gsNRTMytQvi0el51
 ztYBDvugwrN9WtNryKEaqV4bQrlFiWsxremVfcm8FiynGd8HqRUjf271lGXblsJe
 IfcIIVJwnpn0Cd/8SWZp
 =CA9d
 -----END PGP SIGNATURE-----

Merge tag 'omap-for-v3.19/gpmc-timings' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/omap-gpmc

Pull "omap gpmc changes for v3.19" from Tony Lindgren:

GPMC (General Purpose Memory Controller) changes for omaps. These
changes allow us to drop dependencies to bootloader timings now
that the known device tree entries have been fixed. So we can now
require proper timings to be configured and get rid of the legacy
smsc91x code.

Note that this branch has a dependency to the related device tree
branch sent in a separate pull request as timings are now required.

* tag 'omap-for-v3.19/gpmc-timings' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  ARM: OMAP2+: Remove unnecesary include in GPMC driver
  ARM: OMAP2+: Drop legacy code for gpmc-smc91x.c
  ARM: OMAP2+: Require proper GPMC timings for devices
  ARM: OMAP2+: Show bootloader GPMC timings to allow configuring the .dts file
  ARM: OMAP2+: Fix support for multiple devices on a GPMC chip select
  ARM: OMAP2+: gpmc: Sanity check GPMC fck on probe
  ARM: OMAP2+: gpmc: Keep Chip Select disabled while configuring it
  ARM: OMAP2+: gpmc: Always enable A26-A11 for non NAND devices
  ARM: OMAP2+: gpmc: Error out if timings fail in gpmc_probe_generic_child()
  ARM: OMAP2+: gpmc: Print error message in set_gpmc_timing_reg()

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2014-11-20 18:14:47 +01:00
commit 594b732110
6 changed files with 237 additions and 346 deletions

View File

@ -284,9 +284,6 @@ obj-y += $(onenand-m) $(onenand-y)
nand-$(CONFIG_MTD_NAND_OMAP2) := gpmc-nand.o
obj-y += $(nand-m) $(nand-y)
smc91x-$(CONFIG_SMC91X) := gpmc-smc91x.o
obj-y += $(smc91x-m) $(smc91x-y)
smsc911x-$(CONFIG_SMSC911X) := gpmc-smsc911x.o
obj-y += $(smsc911x-m) $(smsc911x-y)
ifneq ($(CONFIG_HWSPINLOCK_OMAP),)

View File

@ -38,7 +38,6 @@
#include <video/omap-panel-data.h>
#include "gpmc.h"
#include "gpmc-smc91x.h"
#include "soc.h"
#include "board-flash.h"
@ -407,32 +406,6 @@ static int __init omap3430_i2c_init(void)
return 0;
}
#if defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE)
static struct omap_smc91x_platform_data board_smc91x_data = {
.cs = 3,
.flags = GPMC_MUX_ADD_DATA | GPMC_TIMINGS_SMC91C96 |
IORESOURCE_IRQ_LOWLEVEL,
};
static void __init board_smc91x_init(void)
{
if (omap_rev() > OMAP3430_REV_ES1_0)
board_smc91x_data.gpio_irq = 6;
else
board_smc91x_data.gpio_irq = 29;
gpmc_smc91x_init(&board_smc91x_data);
}
#else
static inline void board_smc91x_init(void)
{
}
#endif
static void enable_board_wakeup_source(void)
{
/* T2 interrupt line (keypad) */
@ -609,7 +582,6 @@ static void __init omap_3430sdp_init(void)
omap_sdrc_init(hyb18m512160af6_sdrc_params, NULL);
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
usb_musb_init(NULL);
board_smc91x_init();
board_flash_init(sdp_flash_partitions, chip_sel_3430, 0);
sdp3430_display_init();
enable_board_wakeup_source();

View File

@ -32,7 +32,6 @@
#include "common.h"
#include <linux/omap-dma.h>
#include "gpmc-smc91x.h"
#include "board-rx51.h"
@ -1146,33 +1145,6 @@ static struct omap_onenand_platform_data board_onenand_data[] = {
};
#endif
#if defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE)
static struct omap_smc91x_platform_data board_smc91x_data = {
.cs = 1,
.gpio_irq = 54,
.gpio_pwrdwn = 86,
.gpio_reset = 164,
.flags = GPMC_TIMINGS_SMC91C96 | IORESOURCE_IRQ_HIGHLEVEL,
};
static void __init board_smc91x_init(void)
{
omap_mux_init_gpio(54, OMAP_PIN_INPUT_PULLDOWN);
omap_mux_init_gpio(86, OMAP_PIN_OUTPUT);
omap_mux_init_gpio(164, OMAP_PIN_OUTPUT);
gpmc_smc91x_init(&board_smc91x_data);
}
#else
static inline void board_smc91x_init(void)
{
}
#endif
static struct gpio rx51_wl1251_gpios[] __initdata = {
{ RX51_WL1251_IRQ_GPIO, GPIOF_IN, "wl1251 irq" },
};
@ -1303,7 +1275,6 @@ void __init rx51_peripherals_init(void)
rx51_i2c_init();
regulator_has_full_constraints();
gpmc_onenand_init(board_onenand_data);
board_smc91x_init();
rx51_add_gpio_keys();
rx51_init_wl1251();
rx51_init_tsc2005();

View File

@ -1,186 +0,0 @@
/*
* linux/arch/arm/mach-omap2/gpmc-smc91x.c
*
* Copyright (C) 2009 Nokia Corporation
* Contact: Tony Lindgren
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/smc91x.h>
#include "gpmc.h"
#include "gpmc-smc91x.h"
#include "soc.h"
static struct omap_smc91x_platform_data *gpmc_cfg;
static struct resource gpmc_smc91x_resources[] = {
[0] = {
.flags = IORESOURCE_MEM,
},
[1] = {
.flags = IORESOURCE_IRQ,
},
};
static struct smc91x_platdata gpmc_smc91x_info = {
.flags = SMC91X_USE_16BIT | SMC91X_NOWAIT | SMC91X_IO_SHIFT_0,
.leda = RPC_LED_100_10,
.ledb = RPC_LED_TX_RX,
};
static struct platform_device gpmc_smc91x_device = {
.name = "smc91x",
.id = -1,
.dev = {
.platform_data = &gpmc_smc91x_info,
},
.num_resources = ARRAY_SIZE(gpmc_smc91x_resources),
.resource = gpmc_smc91x_resources,
};
static struct gpmc_settings smc91x_settings = {
.device_width = GPMC_DEVWIDTH_16BIT,
};
/*
* Set the gpmc timings for smc91c96. The timings are taken
* from the data sheet available at:
* http://www.smsc.com/main/catalog/lan91c96.html
* REVISIT: Level shifters can add at least to the access latency.
*/
static int smc91c96_gpmc_retime(void)
{
struct gpmc_timings t;
struct gpmc_device_timings dev_t;
const int t3 = 10; /* Figure 12.2 read and 12.4 write */
const int t4_r = 20; /* Figure 12.2 read */
const int t4_w = 5; /* Figure 12.4 write */
const int t5 = 25; /* Figure 12.2 read */
const int t6 = 15; /* Figure 12.2 read */
const int t7 = 5; /* Figure 12.4 write */
const int t8 = 5; /* Figure 12.4 write */
const int t20 = 185; /* Figure 12.2 read and 12.4 write */
/*
* FIXME: Calculate the address and data bus muxed timings.
* Note that at least adv_rd_off needs to be changed according
* to omap3430 TRM Figure 11-11. Are the sdp boards using the
* FPGA in between smc91x and omap as the timings are different
* from above?
*/
if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA)
return 0;
memset(&dev_t, 0, sizeof(dev_t));
dev_t.t_oeasu = t3 * 1000;
dev_t.t_oe = t5 * 1000;
dev_t.t_cez_r = t4_r * 1000;
dev_t.t_oez = t6 * 1000;
dev_t.t_rd_cycle = (t20 - t3) * 1000;
dev_t.t_weasu = t3 * 1000;
dev_t.t_wpl = t7 * 1000;
dev_t.t_wph = t8 * 1000;
dev_t.t_cez_w = t4_w * 1000;
dev_t.t_wr_cycle = (t20 - t3) * 1000;
gpmc_calc_timings(&t, &smc91x_settings, &dev_t);
return gpmc_cs_set_timings(gpmc_cfg->cs, &t);
}
/*
* Initialize smc91x device connected to the GPMC. Note that we
* assume that pin multiplexing is done in the board-*.c file,
* or in the bootloader.
*/
void __init gpmc_smc91x_init(struct omap_smc91x_platform_data *board_data)
{
unsigned long cs_mem_base;
int ret;
gpmc_cfg = board_data;
if (gpmc_cfg->flags & GPMC_TIMINGS_SMC91C96)
gpmc_cfg->retime = smc91c96_gpmc_retime;
if (gpmc_cs_request(gpmc_cfg->cs, SZ_16M, &cs_mem_base) < 0) {
printk(KERN_ERR "Failed to request GPMC mem for smc91x\n");
return;
}
gpmc_smc91x_resources[0].start = cs_mem_base + 0x300;
gpmc_smc91x_resources[0].end = cs_mem_base + 0x30f;
gpmc_smc91x_resources[1].flags |= (gpmc_cfg->flags & IRQF_TRIGGER_MASK);
if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA)
smc91x_settings.mux_add_data = GPMC_MUX_AD;
if (gpmc_cfg->flags & GPMC_READ_MON)
smc91x_settings.wait_on_read = true;
if (gpmc_cfg->flags & GPMC_WRITE_MON)
smc91x_settings.wait_on_write = true;
if (gpmc_cfg->wait_pin)
smc91x_settings.wait_pin = gpmc_cfg->wait_pin;
ret = gpmc_cs_program_settings(gpmc_cfg->cs, &smc91x_settings);
if (ret < 0)
goto free1;
if (gpmc_cfg->retime) {
ret = gpmc_cfg->retime();
if (ret != 0)
goto free1;
}
if (gpio_request_one(gpmc_cfg->gpio_irq, GPIOF_IN, "SMC91X irq") < 0)
goto free1;
gpmc_smc91x_resources[1].start = gpio_to_irq(gpmc_cfg->gpio_irq);
if (gpmc_cfg->gpio_pwrdwn) {
ret = gpio_request_one(gpmc_cfg->gpio_pwrdwn,
GPIOF_OUT_INIT_LOW, "SMC91X powerdown");
if (ret)
goto free2;
}
if (gpmc_cfg->gpio_reset) {
ret = gpio_request_one(gpmc_cfg->gpio_reset,
GPIOF_OUT_INIT_LOW, "SMC91X reset");
if (ret)
goto free3;
gpio_set_value(gpmc_cfg->gpio_reset, 1);
msleep(100);
gpio_set_value(gpmc_cfg->gpio_reset, 0);
}
if (platform_device_register(&gpmc_smc91x_device) < 0) {
printk(KERN_ERR "Unable to register smc91x device\n");
gpio_free(gpmc_cfg->gpio_reset);
goto free3;
}
return;
free3:
if (gpmc_cfg->gpio_pwrdwn)
gpio_free(gpmc_cfg->gpio_pwrdwn);
free2:
gpio_free(gpmc_cfg->gpio_irq);
free1:
gpmc_cs_free(gpmc_cfg->cs);
printk(KERN_ERR "Could not initialize smc91x\n");
}

View File

@ -1,42 +0,0 @@
/*
* arch/arm/plat-omap/include/mach/gpmc-smc91x.h
*
* Copyright (C) 2009 Nokia Corporation
*
* 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.
*/
#ifndef __ASM_ARCH_OMAP_GPMC_SMC91X_H__
#define GPMC_TIMINGS_SMC91C96 (1 << 4)
#define GPMC_MUX_ADD_DATA (1 << 5) /* GPMC_CONFIG1_MUXADDDATA */
#define GPMC_READ_MON (1 << 6) /* GPMC_CONFIG1_WAIT_READ_MON */
#define GPMC_WRITE_MON (1 << 7) /* GPMC_CONFIG1_WAIT_WRITE_MON */
struct omap_smc91x_platform_data {
int cs;
int gpio_irq;
int gpio_pwrdwn;
int gpio_reset;
int wait_pin; /* Optional GPMC_CONFIG1_WAITPINSELECT */
u32 flags;
int (*retime)(void);
};
#if defined(CONFIG_SMC91X) || \
defined(CONFIG_SMC91X_MODULE)
extern void gpmc_smc91x_init(struct omap_smc91x_platform_data *d);
#else
#define board_smc91x_data NULL
static inline void gpmc_smc91x_init(struct omap_smc91x_platform_data *d)
{
}
#endif
#endif

View File

@ -37,7 +37,6 @@
#include <asm/mach-types.h>
#include "soc.h"
#include "common.h"
#include "omap_device.h"
#include "gpmc.h"
#include "gpmc-nand.h"
@ -85,6 +84,8 @@
#define GPMC_ECC_CTRL_ECCREG8 0x008
#define GPMC_ECC_CTRL_ECCREG9 0x009
#define GPMC_CONFIG_LIMITEDADDRESS BIT(1)
#define GPMC_CONFIG2_CSEXTRADELAY BIT(7)
#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7)
#define GPMC_CONFIG4_OEEXTRADELAY BIT(7)
@ -118,6 +119,15 @@
*/
#define GPMC_NR_IRQ 2
struct gpmc_cs_data {
const char *name;
#define GPMC_CS_RESERVED (1 << 0)
u32 flags;
struct resource mem;
};
struct gpmc_client_irq {
unsigned irq;
u32 bitmask;
@ -155,10 +165,9 @@ static struct irq_chip gpmc_irq_chip;
static int gpmc_irq_start;
static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock);
/* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1);
static unsigned int gpmc_cs_num = GPMC_CS_NUM;
static unsigned int gpmc_nr_waitpins;
static struct device *gpmc_dev;
@ -202,11 +211,6 @@ static unsigned long gpmc_get_fclk_period(void)
{
unsigned long rate = clk_get_rate(gpmc_l3_clk);
if (rate == 0) {
printk(KERN_WARNING "gpmc_l3_clk not enabled\n");
return 0;
}
rate /= 1000;
rate = 1000000000 / rate; /* In picoseconds */
@ -284,12 +288,130 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
}
#ifdef DEBUG
static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
bool raw, bool noval, int shift,
const char *name)
{
u32 l;
int nr_bits, max_value, mask;
l = gpmc_cs_read_reg(cs, reg);
nr_bits = end_bit - st_bit + 1;
max_value = (1 << nr_bits) - 1;
mask = max_value << st_bit;
l = (l & mask) >> st_bit;
if (shift)
l = (shift << l);
if (noval && (l == 0))
return 0;
if (!raw) {
unsigned int time_ns_min, time_ns, time_ns_max;
time_ns_min = gpmc_ticks_to_ns(l ? l - 1 : 0);
time_ns = gpmc_ticks_to_ns(l);
time_ns_max = gpmc_ticks_to_ns(l + 1 > max_value ?
max_value : l + 1);
pr_info("gpmc,%s = <%u> (%u - %u ns, %i ticks)\n",
name, time_ns, time_ns_min, time_ns_max, l);
} else {
pr_info("gpmc,%s = <%u>\n", name, l);
}
return l;
}
#define GPMC_PRINT_CONFIG(cs, config) \
pr_info("cs%i %s: 0x%08x\n", cs, #config, \
gpmc_cs_read_reg(cs, config))
#define GPMC_GET_RAW(reg, st, end, field) \
get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 0, 0, field)
#define GPMC_GET_RAW_BOOL(reg, st, end, field) \
get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, 0, field)
#define GPMC_GET_RAW_SHIFT(reg, st, end, shift, field) \
get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, (shift), field)
#define GPMC_GET_TICKS(reg, st, end, field) \
get_gpmc_timing_reg(cs, (reg), (st), (end), 0, 0, 0, field)
static void gpmc_show_regs(int cs, const char *desc)
{
pr_info("gpmc cs%i %s:\n", cs, desc);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG1);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG2);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG3);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG4);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG5);
GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG6);
}
/*
* Note that gpmc,wait-pin handing wrongly assumes bit 8 is available,
* see commit c9fb809.
*/
static void gpmc_cs_show_timings(int cs, const char *desc)
{
gpmc_show_regs(cs, desc);
pr_info("gpmc cs%i access configuration:\n", cs);
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity");
GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data");
GPMC_GET_RAW(GPMC_CS_CONFIG1, 12, 13, "device-width");
GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read");
GPMC_GET_RAW_SHIFT(GPMC_CS_CONFIG1, 23, 24, 4, "burst-length");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 27, 27, "sync-write");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 28, 28, "burst-write");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 29, 29, "gpmc,sync-read");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 30, 30, "burst-read");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 31, 31, "burst-wrap");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG2, 7, 7, "cs-extra-delay");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG3, 7, 7, "adv-extra-delay");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 23, 23, "we-extra-delay");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 7, 7, "oe-extra-delay");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 7, 7, "cycle2cycle-samecsen");
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 6, 6, "cycle2cycle-diffcsen");
pr_info("gpmc cs%i timings configuration:\n", cs);
GPMC_GET_TICKS(GPMC_CS_CONFIG2, 0, 3, "cs-on-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG2, 8, 12, "cs-rd-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG2, 16, 20, "cs-wr-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG3, 0, 3, "adv-on-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG3, 8, 12, "adv-rd-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG3, 16, 20, "adv-wr-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG4, 0, 3, "oe-on-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG4, 8, 12, "oe-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG4, 16, 19, "we-on-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG4, 24, 28, "we-off-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG5, 0, 4, "rd-cycle-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG5, 8, 12, "wr-cycle-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG5, 16, 20, "access-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG5, 24, 27, "page-burst-access-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG1, 18, 19, "wait-monitoring-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG1, 25, 26, "clk-activation-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns");
GPMC_GET_TICKS(GPMC_CS_CONFIG6, 24, 28, "wr-access-ns");
}
#else
static inline void gpmc_cs_show_timings(int cs, const char *desc)
{
}
#endif
static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
int time, const char *name)
#else
static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
int time)
#endif
{
u32 l;
int ticks, mask, nr_bits;
@ -299,15 +421,15 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
else
ticks = gpmc_ns_to_ticks(time);
nr_bits = end_bit - st_bit + 1;
if (ticks >= 1 << nr_bits) {
#ifdef DEBUG
printk(KERN_INFO "GPMC CS%d: %-10s* %3d ns, %3d ticks >= %d\n",
cs, name, time, ticks, 1 << nr_bits);
#endif
mask = (1 << nr_bits) - 1;
if (ticks > mask) {
pr_err("%s: GPMC error! CS%d: %s: %d ns, %d ticks > %d\n",
__func__, cs, name, time, ticks, mask);
return -1;
}
mask = (1 << nr_bits) - 1;
l = gpmc_cs_read_reg(cs, reg);
#ifdef DEBUG
printk(KERN_INFO
@ -322,16 +444,10 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
return 0;
}
#ifdef DEBUG
#define GPMC_SET_ONE(reg, st, end, field) \
if (set_gpmc_timing_reg(cs, (reg), (st), (end), \
t->field, #field) < 0) \
return -1
#else
#define GPMC_SET_ONE(reg, st, end, field) \
if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field) < 0) \
return -1
#endif
int gpmc_calc_divider(unsigned int sync_clk)
{
@ -353,6 +469,7 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
int div;
u32 l;
gpmc_cs_show_timings(cs, "before gpmc_cs_set_timings");
div = gpmc_calc_divider(t->sync_clk);
if (div < 0)
return div;
@ -402,11 +519,12 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
}
gpmc_cs_bool_timings(cs, &t->bool_timings);
gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings");
return 0;
}
static int gpmc_cs_enable_mem(int cs, u32 base, u32 size)
static int gpmc_cs_set_memconf(int cs, u32 base, u32 size)
{
u32 l;
u32 mask;
@ -430,6 +548,15 @@ static int gpmc_cs_enable_mem(int cs, u32 base, u32 size)
return 0;
}
static void gpmc_cs_enable_mem(int cs)
{
u32 l;
l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
l |= GPMC_CONFIG7_CSVALID;
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
}
static void gpmc_cs_disable_mem(int cs)
{
u32 l;
@ -460,13 +587,30 @@ static int gpmc_cs_mem_enabled(int cs)
static void gpmc_cs_set_reserved(int cs, int reserved)
{
gpmc_cs_map &= ~(1 << cs);
gpmc_cs_map |= (reserved ? 1 : 0) << cs;
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
gpmc->flags |= GPMC_CS_RESERVED;
}
static bool gpmc_cs_reserved(int cs)
{
return gpmc_cs_map & (1 << cs);
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
return gpmc->flags & GPMC_CS_RESERVED;
}
static void gpmc_cs_set_name(int cs, const char *name)
{
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
gpmc->name = name;
}
const char *gpmc_cs_get_name(int cs)
{
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
return gpmc->name;
}
static unsigned long gpmc_mem_align(unsigned long size)
@ -485,7 +629,8 @@ static unsigned long gpmc_mem_align(unsigned long size)
static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)
{
struct resource *res = &gpmc_cs_mem[cs];
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
struct resource *res = &gpmc->mem;
int r;
size = gpmc_mem_align(size);
@ -500,7 +645,8 @@ static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)
static int gpmc_cs_delete_mem(int cs)
{
struct resource *res = &gpmc_cs_mem[cs];
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
struct resource *res = &gpmc->mem;
int r;
spin_lock(&gpmc_mem_lock);
@ -541,23 +687,24 @@ static int gpmc_cs_remap(int cs, u32 base)
gpmc_cs_get_memconf(cs, &old_base, &size);
if (base == old_base)
return 0;
gpmc_cs_disable_mem(cs);
ret = gpmc_cs_delete_mem(cs);
if (ret < 0)
return ret;
ret = gpmc_cs_insert_mem(cs, base, size);
if (ret < 0)
return ret;
ret = gpmc_cs_enable_mem(cs, base, size);
if (ret < 0)
return ret;
return 0;
ret = gpmc_cs_set_memconf(cs, base, size);
return ret;
}
int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
{
struct resource *res = &gpmc_cs_mem[cs];
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
struct resource *res = &gpmc->mem;
int r = -1;
if (cs > gpmc_cs_num) {
@ -581,12 +728,17 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
if (r < 0)
goto out;
r = gpmc_cs_enable_mem(cs, res->start, resource_size(res));
/* Disable CS while changing base address and size mask */
gpmc_cs_disable_mem(cs);
r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
if (r < 0) {
release_resource(res);
goto out;
}
/* Enable CS */
gpmc_cs_enable_mem(cs);
*base = res->start;
gpmc_cs_set_reserved(cs, 1);
out:
@ -597,7 +749,8 @@ EXPORT_SYMBOL(gpmc_cs_request);
void gpmc_cs_free(int cs)
{
struct resource *res = &gpmc_cs_mem[cs];
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
struct resource *res = &gpmc->mem;
spin_lock(&gpmc_mem_lock);
if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
@ -1511,7 +1664,9 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
struct gpmc_timings gpmc_t;
struct resource res;
unsigned long base;
const char *name;
int ret, cs;
u32 val;
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n",
@ -1525,28 +1680,41 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
return -ENODEV;
}
/*
* Check if we have multiple instances of the same device
* on a single chip select. If so, use the already initialized
* timings.
*/
name = gpmc_cs_get_name(cs);
if (name && child->name && of_node_cmp(child->name, name) == 0)
goto no_timings;
ret = gpmc_cs_request(cs, resource_size(&res), &base);
if (ret < 0) {
dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
return ret;
}
gpmc_cs_set_name(cs, child->name);
gpmc_read_settings_dt(child, &gpmc_s);
gpmc_read_timings_dt(child, &gpmc_t);
/*
* For some GPMC devices we still need to rely on the bootloader
* timings because the devices can be connected via FPGA. So far
* the list is smc91x on the omap2 SDP boards, and 8250 on zooms.
* REVISIT: Add timing support from slls644g.pdf and from the
* lan91c96 manual.
* timings because the devices can be connected via FPGA.
* REVISIT: Add timing support from slls644g.pdf.
*/
if (of_device_is_compatible(child, "ns16550a") ||
of_device_is_compatible(child, "smsc,lan91c94") ||
of_device_is_compatible(child, "smsc,lan91c111")) {
dev_warn(&pdev->dev,
"%s using bootloader timings on CS%d\n",
child->name, cs);
if (!gpmc_t.cs_rd_off) {
WARN(1, "enable GPMC debug to configure .dts timings for CS%i\n",
cs);
gpmc_cs_show_timings(cs,
"please add GPMC bootloader timings to .dts");
goto no_timings;
}
/* CS must be disabled while making changes to gpmc configuration */
gpmc_cs_disable_mem(cs);
/*
* FIXME: gpmc_cs_request() will map the CS to an arbitary
* location in the gpmc address space. When booting with
@ -1562,8 +1730,6 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
goto err;
}
gpmc_read_settings_dt(child, &gpmc_s);
ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width);
if (ret < 0)
goto err;
@ -1572,8 +1738,20 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
if (ret < 0)
goto err;
gpmc_read_timings_dt(child, &gpmc_t);
gpmc_cs_set_timings(cs, &gpmc_t);
ret = gpmc_cs_set_timings(cs, &gpmc_t);
if (ret) {
dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
child->name);
goto err;
}
/* Clear limited address i.e. enable A26-A11 */
val = gpmc_read_reg(GPMC_CONFIG);
val &= ~GPMC_CONFIG_LIMITEDADDRESS;
gpmc_write_reg(GPMC_CONFIG, val);
/* Enable CS region */
gpmc_cs_enable_mem(cs);
no_timings:
if (of_platform_device_create(child, NULL, &pdev->dev))
@ -1670,13 +1848,18 @@ static int gpmc_probe(struct platform_device *pdev)
else
gpmc_irq = res->start;
gpmc_l3_clk = clk_get(&pdev->dev, "fck");
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(gpmc_l3_clk)) {
dev_err(&pdev->dev, "error: clk_get\n");
dev_err(&pdev->dev, "Failed to get GPMC fck\n");
gpmc_irq = 0;
return PTR_ERR(gpmc_l3_clk);
}
if (!clk_get_rate(gpmc_l3_clk)) {
dev_err(&pdev->dev, "Invalid GPMC fck clock rate\n");
return -EINVAL;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@ -1708,9 +1891,6 @@ static int gpmc_probe(struct platform_device *pdev)
if (gpmc_setup_irq() < 0)
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
/* Now the GPMC is initialised, unreserve the chip-selects */
gpmc_cs_map = 0;
if (!pdev->dev.of_node) {
gpmc_cs_num = GPMC_CS_NUM;
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
@ -1719,7 +1899,6 @@ static int gpmc_probe(struct platform_device *pdev)
rc = gpmc_probe_dt(pdev);
if (rc < 0) {
pm_runtime_put_sync(&pdev->dev);
clk_put(gpmc_l3_clk);
dev_err(gpmc_dev, "failed to probe DT parameters\n");
return rc;
}