/* * The first 1MB of GPMC address space is typically mapped to * the internal ROM. Never allocate the first page, to * facilitate bug detection; even if we didn't boot from ROM. * As GPMC minimum partition size is 16MB we can only start from * there.
*/ #define GPMC_MEM_START 0x1000000 #define GPMC_MEM_END 0x3FFFFFFF
/* TODO: Add support for gpmc_fck to clock framework and use it */ staticunsignedlong gpmc_get_fclk_period(void)
{ unsignedlong rate = clk_get_rate(gpmc_l3_clk);
/** * gpmc_get_clk_period - get period of selected clock domain in ps * @cs: Chip Select Region. * @cd: Clock Domain. * * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup * prior to calling this function with GPMC_CD_CLK.
*/ staticunsignedlong gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
{ unsignedlong tick_ps = gpmc_get_fclk_period();
u32 l; int div;
switch (cd) { case GPMC_CD_CLK: /* get current clk divider */
l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
div = (l & 0x03) + 1; /* get GPMC_CLK period */
tick_ps *= div; break; case GPMC_CD_FCLK: default: break;
}
/** * get_gpmc_timing_reg - read a timing parameter and print DTS settings for it. * @cs: Chip Select Region * @reg: GPMC_CS_CONFIGn register offset. * @st_bit: Start Bit * @end_bit: End Bit. Must be >= @st_bit. * @max: Maximum parameter value (before optional @shift). * If 0, maximum is as high as @st_bit and @end_bit allow. * @name: DTS node name, w/o "gpmc," * @cd: Clock Domain of timing parameter. * @shift: Parameter value left shifts @shift, which is then printed instead of value. * @raw: Raw Format Option. * raw format: gpmc,name = <value> * tick format: gpmc,name = <value> /* x ns -- y ns; x ticks */ * Where x ns -- y ns result in the same tick value. * When @max is exceeded, "invalid" is printed inside comment. * @noval: Parameter values equal to 0 are not printed. * @return: Specified timing parameter (after optional @shift). *
*/ staticint get_gpmc_timing_reg( /* timing specifiers */ int cs, int reg, int st_bit, int end_bit, int max, constchar *name, constenum gpmc_clk_domain cd, /* value transform */ int shift, /* format specifiers */ bool raw, bool noval)
{
u32 l; int nr_bits; int mask; bool invalid;
l = gpmc_cs_read_reg(cs, reg);
nr_bits = end_bit - st_bit + 1;
mask = (1 << nr_bits) - 1;
l = (l >> st_bit) & mask; if (!max)
max = mask;
invalid = l > max; if (shift)
l = (shift << l); if (noval && (l == 0)) return 0; if (!raw) { /* DTS tick format for timings in ns */ unsignedint time_ns; unsignedint time_ns_min = 0;
/** * set_gpmc_timing_reg - set a single timing parameter for Chip Select Region. * Caller is expected to have initialized CONFIG1 GPMCFCLKDIVIDER * prior to calling this function with @cd equal to GPMC_CD_CLK. * * @cs: Chip Select Region. * @reg: GPMC_CS_CONFIGn register offset. * @st_bit: Start Bit * @end_bit: End Bit. Must be >= @st_bit. * @max: Maximum parameter value. * If 0, maximum is as high as @st_bit and @end_bit allow. * @time: Timing parameter in ns. * @cd: Timing parameter clock domain. * @name: Timing parameter name. * @return: 0 on success, -1 on error.
*/ staticint set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max, int time, enum gpmc_clk_domain cd, constchar *name)
{
u32 l; int ticks, mask, nr_bits;
/** * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. * read --> don't sample bus too early * write --> data is longer on bus * * Formula: * gpmc_clk_div + 1 = ceil(ceil(waitmonitoringtime_ns / gpmc_fclk_ns) * / waitmonitoring_ticks) * WAITMONITORINGTIME resulting in 0 or 1 tick with div = 1 are caught by * div <= 0 check. * * @wait_monitoring: WAITMONITORINGTIME in ns. * @return: -1 on failure to scale, else proper divider > 0.
*/ staticint gpmc_calc_waitmonitoring_divider(unsignedint wait_monitoring)
{ int div = gpmc_ns_to_ticks(wait_monitoring);
div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1;
div /= GPMC_CONFIG1_WAITMONITORINGTIME_MAX;
if (div > 4) return -1; if (div <= 0)
div = 1;
return div;
}
/** * gpmc_calc_divider - calculate GPMC_FCLK divider for sync_clk GPMC_CLK period. * @sync_clk: GPMC_CLK period in ps. * @return: Returns at least 1 if GPMC_FCLK can be divided to GPMC_CLK. * Else, returns -1.
*/ int gpmc_calc_divider(unsignedint sync_clk)
{ int div = gpmc_ps_to_ticks(sync_clk);
if (div > 4) return -1; if (div <= 0)
div = 1;
return div;
}
/** * gpmc_cs_set_timings - program timing parameters for Chip Select Region. * @cs: Chip Select Region. * @t: GPMC timing parameters. * @s: GPMC timing settings. * @return: 0 on success, -1 on error.
*/ int gpmc_cs_set_timings(int cs, conststruct gpmc_timings *t, conststruct gpmc_settings *s)
{ int div, ret;
u32 l;
div = gpmc_calc_divider(t->sync_clk); if (div < 0) return -EINVAL;
/* * See if we need to change the divider for waitmonitoringtime. * * Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for * pure asynchronous accesses, i.e. both read and write asynchronous. * However, only do so if WAITMONITORINGTIME is actually used, i.e. * either WAITREADMONITORING or WAITWRITEMONITORING is set. * * This statement must not change div to scale async WAITMONITORINGTIME * to protect mixed synchronous and asynchronous accesses. * * We raise an error later if WAITMONITORINGTIME does not fit.
*/ if (!s->sync_read && !s->sync_write &&
(s->wait_on_read || s->wait_on_write)
) {
div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring); if (div < 0) {
pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n",
__func__,
t->wait_monitoring
); return -ENXIO;
}
}
ret = 0;
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 0, 3, 0, t->cs_on,
GPMC_CD_FCLK, "cs_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 8, 12, 0, t->cs_rd_off,
GPMC_CD_FCLK, "cs_rd_off");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 16, 20, 0, t->cs_wr_off,
GPMC_CD_FCLK, "cs_wr_off"); if (ret) return -ENXIO;
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 0, 3, 0, t->adv_on,
GPMC_CD_FCLK, "adv_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 8, 12, 0, t->adv_rd_off,
GPMC_CD_FCLK, "adv_rd_off");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 16, 20, 0, t->adv_wr_off,
GPMC_CD_FCLK, "adv_wr_off"); if (ret) return -ENXIO;
if (gpmc_capability & GPMC_HAS_MUX_AAD) {
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 4, 6, 0,
t->adv_aad_mux_on, GPMC_CD_FCLK, "adv_aad_mux_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 24, 26, 0,
t->adv_aad_mux_rd_off, GPMC_CD_FCLK, "adv_aad_mux_rd_off");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 28, 30, 0,
t->adv_aad_mux_wr_off, GPMC_CD_FCLK, "adv_aad_mux_wr_off"); if (ret) return -ENXIO;
}
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 0, 3, 0, t->oe_on,
GPMC_CD_FCLK, "oe_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 8, 12, 0, t->oe_off,
GPMC_CD_FCLK, "oe_off"); if (gpmc_capability & GPMC_HAS_MUX_AAD) {
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 4, 6, 0,
t->oe_aad_mux_on, GPMC_CD_FCLK, "oe_aad_mux_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 13, 15, 0,
t->oe_aad_mux_off, GPMC_CD_FCLK, "oe_aad_mux_off");
}
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 16, 19, 0, t->we_on,
GPMC_CD_FCLK, "we_on");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 24, 28, 0, t->we_off,
GPMC_CD_FCLK, "we_off"); if (ret) return -ENXIO;
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 0, 4, 0, t->rd_cycle,
GPMC_CD_FCLK, "rd_cycle");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 8, 12, 0, t->wr_cycle,
GPMC_CD_FCLK, "wr_cycle");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 16, 20, 0, t->access,
GPMC_CD_FCLK, "access");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 24, 27, 0,
t->page_burst_access, GPMC_CD_FCLK, "page_burst_access"); if (ret) return -ENXIO;
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 0, 3, 0,
t->bus_turnaround, GPMC_CD_FCLK, "bus_turnaround");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 8, 11, 0,
t->cycle2cycle_delay, GPMC_CD_FCLK, "cycle2cycle_delay"); if (ret) return -ENXIO;
if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) {
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 16, 19, 0,
t->wr_data_mux_bus, GPMC_CD_FCLK, "wr_data_mux_bus"); if (ret) return -ENXIO;
} if (gpmc_capability & GPMC_HAS_WR_ACCESS) {
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 24, 28, 0,
t->wr_access, GPMC_CD_FCLK, "wr_access"); if (ret) return -ENXIO;
}
l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
l &= ~0x03;
l |= (div - 1);
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
ret = 0;
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 18, 19,
GPMC_CONFIG1_WAITMONITORINGTIME_MAX,
t->wait_monitoring, GPMC_CD_CLK, "wait_monitoring");
ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 25, 26,
GPMC_CONFIG1_CLKACTIVATIONTIME_MAX,
t->clk_activation, GPMC_CD_FCLK, "clk_activation"); if (ret) return -ENXIO;
#ifdef CONFIG_OMAP_GPMC_DEBUG
pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n",
cs, (div * gpmc_get_fclk_period()) / 1000, div); #endif
l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
l &= ~GPMC_CONFIG7_MASK;
l |= base & GPMC_CONFIG7_BASEADDRESS_MASK;
l |= mask & GPMC_CONFIG7_MASKADDRESS_MASK;
l |= GPMC_CONFIG7_CSVALID;
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
return 0;
}
staticvoid 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);
}
staticvoid gpmc_cs_disable_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);
}
staticvoid gpmc_free_waitpin(struct gpmc_device *gpmc, int wait_pin)
{ if (gpmc_is_valid_waitpin(wait_pin))
gpiochip_free_own_desc(gpmc->waitpins[wait_pin].desc);
}
/** * gpmc_configure - write request to configure gpmc * @cmd: command type * @wval: value to write * @return status of the operation
*/ int gpmc_configure(int cmd, int wval)
{
u32 regval;
switch (cmd) { case GPMC_CONFIG_WP:
regval = gpmc_read_reg(GPMC_CONFIG); if (wval)
regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */ else
regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */
gpmc_write_reg(GPMC_CONFIG, regval); break;
default:
pr_err("%s: command not supported\n", __func__); return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(gpmc_configure);
staticbool gpmc_nand_writebuffer_empty(void)
{ if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS) returntrue;
/** * gpmc_omap_get_nand_ops - Get the GPMC NAND interface * @reg: the GPMC NAND register map exclusive for NAND use. * @cs: GPMC chip select number on which the NAND sits. The * register map returned will be specific to this chip select. * * Returns NULL on error e.g. invalid cs.
*/ struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
{ int i;
int gpmc_omap_onenand_set_timings(struct device *dev, int cs, int freq, int latency, struct gpmc_onenand_info *info)
{ int ret; struct gpmc_timings gpmc_t; struct gpmc_settings gpmc_s;
/* Setting bit to 1 clears (or Acks) the interrupt */
gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq));
}
staticint gpmc_irq_set_type(struct irq_data *d, unsignedint trigger)
{ /* can't set type for NAND IRQs */ if (d->hwirq < GPMC_NR_NAND_IRQS) return -EINVAL;
/* We can support either rising or falling edge at a time */ if (trigger == IRQ_TYPE_EDGE_FALLING)
gpmc_irq_edge_config(d->hwirq, false); elseif (trigger == IRQ_TYPE_EDGE_RISING)
gpmc_irq_edge_config(d->hwirq, true); else return -EINVAL;
div = gpmc_calc_divider(sync_clk);
temp = gpmc_ps_to_ticks(time_ps);
temp = (temp + div - 1) / div; return gpmc_ticks_to_ps(temp * div);
}
/* XXX: can the cycles be avoided ? */ staticint gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t, struct gpmc_device_timings *dev_t, bool mux)
{
u32 temp;
/* adv_rd_off */
temp = dev_t->t_avdp_r; /* XXX: mux check required ? */ if (mux) { /* XXX: t_avdp not to be required for sync, only added for tusb * this indirectly necessitates requirement of t_avdp_r and * t_avdp_w instead of having a single t_avdp
*/
temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_avdh);
temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
}
gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp);
if (gpmc_calc_divider(gpmc_t->sync_clk) != 1) return 0;
if (dev_t->ce_xdelay)
gpmc_t->bool_timings.cs_extra_delay = true; if (dev_t->avd_xdelay)
gpmc_t->bool_timings.adv_extra_delay = true; if (dev_t->oe_xdelay)
gpmc_t->bool_timings.oe_extra_delay = true; if (dev_t->we_xdelay)
gpmc_t->bool_timings.we_extra_delay = true;
/* TODO: remove, see function definition */
gpmc_convert_ps_to_ns(gpmc_t);
return 0;
}
/** * gpmc_cs_program_settings - programs non-timing related settings * @cs: GPMC chip-select to program * @p: pointer to GPMC settings structure * * Programs non-timing related settings for a GPMC chip-select, such as * bus-width, burst configuration, etc. Function should be called once * for each chip-select that is being used and must be called before * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1 * register will be initialised to zero by this function. Returns 0 on * success and appropriate negative error code on failure.
*/ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
{
u32 config1;
/** * gpmc_cs_remap - remaps a chip-select physical base address * @cs: chip-select to remap * @base: physical base address to re-map chip-select to * * Re-maps a chip-select to a new physical base address specified by * "base". Returns 0 on success and appropriate negative error code * on failure.
*/ staticint gpmc_cs_remap(int cs, u32 base)
{ int ret;
u32 old_base, size;
if (cs >= gpmc_cs_num) {
pr_err("%s: requested chip-select is disabled\n", __func__); return -ENODEV;
}
/* * Make sure we ignore any device offsets from the GPMC partition * allocated for the chip select and that the new base confirms * to the GPMC 16MB minimum granularity.
*/
base &= ~(SZ_16M - 1);
gpmc_cs_get_memconf(cs, &old_base, &size); if (base == old_base) return 0;
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_set_memconf(cs, base, size);
return ret;
}
/** * gpmc_read_settings_dt - read gpmc settings from device-tree * @np: pointer to device-tree node for a gpmc child device * @p: pointer to gpmc settings structure * * Reads the GPMC settings for a GPMC child device from device-tree and * stores them in the GPMC settings structure passed. The GPMC settings * structure is initialised to zero by this function and so any * previously stored settings will be cleared.
*/ void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
{
memset(p, 0, sizeof(struct gpmc_settings));
/** * gpmc_probe_generic_child - configures the gpmc for a child device * @pdev: pointer to gpmc platform device * @child: pointer to device-tree node for child device * * Allocates and configures a GPMC chip-select for a child device. * Returns 0 on success and appropriate negative error code on failure.
*/ staticint gpmc_probe_generic_child(struct platform_device *pdev, struct device_node *child)
{ struct gpmc_settings gpmc_s; struct gpmc_timings gpmc_t; struct resource res; unsignedlong base; constchar *name; int ret, cs;
u32 val; struct gpmc_device *gpmc = platform_get_drvdata(pdev);
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(&pdev->dev, "%pOF has no 'reg' property\n",
child); return -ENODEV;
}
if (of_address_to_resource(child, 0, &res) < 0) {
dev_err(&pdev->dev, "%pOF has malformed 'reg' property\n",
child); 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 && of_node_name_eq(child, name)) goto no_timings;
/* * For some GPMC devices we still need to rely on the bootloader * timings because the devices can be connected via FPGA. * REVISIT: Add timing support from slls644g.pdf.
*/ 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 arbitrary * location in the gpmc address space. When booting with * device-tree we want the NOR flash to be mapped to the * location specified in the device-tree blob. So remap the * CS to this location. Once DT migration is complete should * just make gpmc_cs_request() map a specific address.
*/
ret = gpmc_cs_remap(cs, res.start); if (ret < 0) {
dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
cs, &res.start); if (res.start < GPMC_MEM_START) {
dev_info(&pdev->dev, "GPMC CS %d start cannot be lesser than 0x%x\n",
cs, GPMC_MEM_START);
} elseif (res.end > GPMC_MEM_END) {
dev_info(&pdev->dev, "GPMC CS %d end cannot be greater than 0x%x\n",
cs, GPMC_MEM_END);
} goto err;
}
if (of_match_node(omap_nand_ids, child)) { /* NAND specific setup */
val = 8;
of_property_read_u32(child, "nand-bus-width", &val); switch (val) { case 8:
gpmc_s.device_width = GPMC_DEVWIDTH_8BIT; break; case 16:
gpmc_s.device_width = GPMC_DEVWIDTH_16BIT; break; default:
dev_err(&pdev->dev, "%pOFn: invalid 'nand-bus-width'\n",
child);
ret = -EINVAL; goto err;
}
/* disable write protect */
gpmc_configure(GPMC_CONFIG_WP, 0);
gpmc_s.device_nand = true;
} else {
ret = of_property_read_u32(child, "bank-width",
&gpmc_s.device_width); if (ret < 0 && !gpmc_s.device_width) {
dev_err(&pdev->dev, "%pOF has no 'gpmc,device-width' property\n",
child); goto err;
}
}
/* Reserve wait pin if it is required and valid */ if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) {
ret = gpmc_alloc_waitpin(gpmc, &gpmc_s); if (ret < 0) goto err;
}
ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs",
&gpmc_cs_num); if (ret < 0) {
pr_err("%s: number of chip-selects not defined\n", __func__); return ret;
} elseif (gpmc_cs_num < 1) {
pr_err("%s: all chip-selects are disabled\n", __func__); return -EINVAL;
} elseif (gpmc_cs_num > GPMC_CS_NUM) {
pr_err("%s: number of supported chip-selects cannot be > %d\n",
__func__, GPMC_CS_NUM); return -EINVAL;
}
ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins",
&gpmc_nr_waitpins); if (ret < 0) {
pr_err("%s: number of wait pins not found!\n", __func__); return ret;
}
return 0;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.55Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.