NetBSD/sys/arch/arm/omap/omap_emifs.c

406 lines
13 KiB
C

/* $NetBSD: omap_emifs.c,v 1.5 2011/07/01 20:30:21 dyoung Exp $ */
/*
* Autoconfiguration support for the Texas Instruments OMAP EMIFS bus.
* Based on arm/xscale/pxa2x0.c which in turn was derived
* from arm/sa11x0/sa11x0.c
*
* Copyright (c) 2002, 2005 Genetec Corporation. All rights reserved.
* Written by Hiroyuki Bessho for Genetec Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Genetec Corporation.
* 4. The name of Genetec Corporation may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Copyright (c) 1997,1998, 2001, The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by IWAMOTO Toshihiro, Ichiro FUKUHARA and Paul Kranenburg.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Copyright (c) 1999
* Shin Takemura and PocketBSD Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the PocketBSD project
* and its contributors.
* 4. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: omap_emifs.c,v 1.5 2011/07/01 20:30:21 dyoung Exp $");
#include "locators.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/reboot.h>
#include <machine/cpu.h>
#include <sys/bus.h>
#include <arm/cpufunc.h>
#include <arm/mainbus/mainbus.h>
#include <arm/omap/omap_reg.h>
#include <arm/omap/omap_emifs.h>
struct emifs_softc {
device_t sc_dev;
bus_dma_tag_t sc_dmac;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
};
enum which_clock { TC_CLK, REF_CLK };
enum which_reg { CCS, ACS };
typedef struct timing_parm_info {
const char *name; /* name of parm */
int cf_ndx; /* Index to get parm */
enum which_clock clk; /* TC_CLK or REF_CLK */
enum which_reg reg; /* CCS or ACS? */
u_int shift; /* Field's register position */
u_int adder; /* Number of cycles to add */
u_int max; /* Maximum field value */
} timing_parm_info;
/* prototypes */
static int emifs_match(device_t, cfdata_t, void *);
static void emifs_attach(device_t, device_t, void *);
static u_int emifs_cvt_nsec(const timing_parm_info *, u_int, int);
static void emifs_set_timing(struct emifs_softc *, cfdata_t );
static int emifs_search(device_t, cfdata_t,
const int *, void *);
static int emifs_print(void *, const char *);
#ifndef OMAP_TC_CLOCK_FREQ
#error Specify the TC clock frequency in Hz with the OMAP_TC_CLOCK_FREQ option.
#endif
/* Encapsulate the device knowledge within this source. */
/* Register offsets, values and information needed to set them. */
#define EMIFS_CCS(cs) (0x10+((cs)*4)) /* chip-select config */
#define EMIFS_ACS(cs) (0x50+((cs)*4)) /* advanced chip-select config */
#define EMIFS_ACS_BTMODE (1<<9)
#define EMIFS_SIZE 256 /* per TRM */
static const timing_parm_info timing_parms[] = {
{ "rdwst", EMIFSCF_RDWST, REF_CLK, CCS, 4, 2, 0xF },
{ "oesetup", EMIFSCF_OESETUP, REF_CLK, ACS, 0, 0, 0xF },
{ "oehold", EMIFSCF_OEHOLD, REF_CLK, ACS, 4, 0, 0xF },
{ "wrwst", EMIFSCF_WRWST, REF_CLK, CCS, 8, 1, 0xF },
{ "welen", EMIFSCF_WELEN, REF_CLK, CCS, 12, 1, 0xF },
{ "advhold", EMIFSCF_ADVHOLD, REF_CLK, ACS, 8, 1, 0x1 },
{ "btwst", EMIFSCF_BTWST, TC_CLK, CCS, 23, 1, 0xF }
};
/* attach structures */
CFATTACH_DECL_NEW(emifs, sizeof(struct emifs_softc),
emifs_match, emifs_attach, NULL, NULL);
static int emifs_attached;
static int
emifs_match(device_t parent, cfdata_t match, void *aux)
{
if (emifs_attached)
return 0;
return 1;
}
static void
emifs_attach(device_t parent, device_t self, void *aux)
{
struct emifs_softc *sc = device_private(self);
struct mainbus_attach_args *mainbus = aux;
sc->sc_dev = self;
/*
* mainbus->mb_iot always multiplies the offset by 4 and doesn't seem
* to be widely used, so I'm just going to use the omap bus.
*/
if (mainbus->mb_iobase != -1) {
/*
* The address is only needed for modifying the timings, so
* don't make it mandatory. We'll check to see if it was set
* when (and if) it is needed.
*/
sc->sc_iot = &omap_bs_tag;
if (bus_space_map(sc->sc_iot,
mainbus->mb_iobase, EMIFS_SIZE,
0, &sc->sc_ioh))
panic("%s: Cannot map registers", device_xname(self));
} else
sc->sc_iot = NULL;
emifs_attached = 1;
#if NOMAPDMAC > 0
#error DMA not implemented
sc->sc_dmac = &omap_bus_dma_tag;
#else
sc->sc_dmac = NULL;
#endif
aprint_normal(": Extended Memory Interface Slow\n");
aprint_naive("\n");
/*
* Attach all our devices
*/
config_search_ia(emifs_search, self, "emifs", NULL);
}
static const u_int ns_per_sec = 1000000000;
static u_int
emifs_cvt_nsec(const timing_parm_info *tp, u_int source_freq, int nsec)
{
u_int desired_freq, clocks, rval;
/*
* It is easier to work with a frequency in Hz, instead of a
* period in nanoseconds.
*/
desired_freq = ns_per_sec / nsec;
/*
* Then we can just divide the source frequency by the desired
* frequency to get the number of clocks. If it doesn't divide
* evenly, round up to make sure the period is long enough.
*/
clocks = source_freq / desired_freq;
if ((clocks * desired_freq) != source_freq)
clocks++;
/* Adjust for the number of cycles that are added. */
rval = clocks - tp->adder;
/* Limit to maximum */
if (rval > tp->max) {
aprint_error("EMIFS: %s: %d ns is too large.\n",
tp->name, nsec);
rval = tp->max;
}
return rval;
}
static void
emifs_set_timing(struct emifs_softc *sc, cfdata_t cf)
{
static const u_int tc_freq = OMAP_TC_CLOCK_FREQ;
/* We force REF to be the same frequency as TC. */
static const u_int ref_freq = tc_freq;
int cs, i;
uint32_t ccs, acs;
/*
* Ensure we have what everything we need to set the EMIFS bus
* timing parameters.
*/
cs = cf->cf_loc[EMIFSCF_CS];
if (cs < 0 || cs > 3)
panic("cs parameter must be in the range of 0 to 3.");
if (sc->sc_iot == NULL)
panic("Parent emifs device must have base specified.");
ccs = 0;
acs = 0;
switch (cf->cf_loc[EMIFSCF_BTMODE]) {
case -1:
case 0:
break;
case 1:
acs = EMIFS_ACS_BTMODE;
break;
default:
panic("btmode must be 0, 1 or not given.");
}
aprint_verbose("EMIFS: TC_CK period: %u ns\n", ns_per_sec / tc_freq);
aprint_verbose("EMIFS: REF_CK period: %u ns\n", ns_per_sec / ref_freq);
for (i = 0; i < __arraycount(timing_parms); i++) {
const timing_parm_info *tp;
int nsec;
u_int source_freq, field_val;
tp = &timing_parms[i];
nsec = cf->cf_loc[tp->cf_ndx];
/* Blow up on completely wrong parameters. */
if (nsec < 0 || nsec > OMAP_TC_CLOCK_FREQ)
panic("Invalid %s period of %d nsec.", tp->name, nsec);
if (tp->clk == REF_CLK)
source_freq = ref_freq;
else
source_freq = tc_freq;
if (nsec == 0)
/*
* Handle the zero case separately because: 1) it
* avoids a divide by zero case, 2) we already know
* the answer is zero, and 3) we know the field is
* already zeroed.
*/
field_val = 0;
else {
field_val = emifs_cvt_nsec(tp, source_freq, nsec);
if (tp->reg == CCS)
ccs |= (field_val << tp->shift);
else
acs |= (field_val << tp->shift);
}
/*
* Tell them what they got. The ">> 5"'s are to prevent
* overflow. We know ns_per_sec fits in a word, and we know
* that (field_val + tp->addr) is less than five bits. If we
* shift the numerator and denominator the same amount, we'll
* get the same answer, but without overflow.
*/
aprint_verbose("EMIFS: %8s: Requested %4u ns. Got %4u ns.\n",
tp->name, nsec,
((field_val + tp->adder) * (ns_per_sec >> 5)
/ (source_freq >> 5)));
}
/* Now tell the hardware what we figured out. */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMIFS_CCS(cs), ccs);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMIFS_ACS(cs), acs);
}
static int
emifs_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
{
struct emifs_softc *sc = device_private(parent);
struct emifs_attach_args aa;
/* Set up the attach args. */
if (cf->cf_loc[EMIFSCF_NOBYTEACC] == 1) {
if (cf->cf_loc[EMIFSCF_MULT] == 1)
aa.emifs_iot = &nobyteacc_bs_tag;
else
panic("nobyteacc specified for device with non-byte multiplier\n");
} else {
switch (cf->cf_loc[EMIFSCF_MULT]) {
case 1:
aa.emifs_iot = &omap_bs_tag;
break;
case 2:
aa.emifs_iot = &omap_a2x_bs_tag;
break;
case 4:
aa.emifs_iot = &omap_a4x_bs_tag;
break;
default:
panic("Unsupported EMIFS multiplier.");
break;
}
}
aa.emifs_dmac = sc->sc_dmac;
aa.emifs_addr = cf->cf_loc[EMIFSCF_ADDR];
aa.emifs_size = cf->cf_loc[EMIFSCF_SIZE];
aa.emifs_intr = cf->cf_loc[EMIFSCF_INTR];
/* Chip-select specified? */
if (cf->cf_loc[EMIFSCF_CS] != -1)
emifs_set_timing(sc, cf);
if (config_match(parent, cf, &aa))
config_attach(parent, cf, &aa, emifs_print);
return 0;
}
static int
emifs_print(void *aux, const char *name)
{
struct emifs_attach_args *sa = (struct emifs_attach_args*)aux;
if (sa->emifs_addr != EMIFSCF_ADDR_DEFAULT) {
aprint_normal(" addr 0x%08lx", sa->emifs_addr);
if (sa->emifs_size > EMIFSCF_SIZE_DEFAULT)
aprint_normal("-0x%08lx", sa->emifs_addr + sa->emifs_size-1);
}
if (sa->emifs_intr != EMIFSCF_INTR_DEFAULT)
aprint_normal(" intr %d", sa->emifs_intr);
return (UNCONF);
}