linux/drivers/mfd/atmel-smc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Atmel SMC (Static Memory Controller) helper functions.
   4 *
   5 * Copyright (C) 2017 Atmel
   6 * Copyright (C) 2017 Free Electrons
   7 *
   8 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
   9 */
  10
  11#include <linux/mfd/syscon/atmel-smc.h>
  12#include <linux/string.h>
  13
  14/**
  15 * atmel_smc_cs_conf_init - initialize a SMC CS conf
  16 * @conf: the SMC CS conf to initialize
  17 *
  18 * Set all fields to 0 so that one can start defining a new config.
  19 */
  20void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
  21{
  22        memset(conf, 0, sizeof(*conf));
  23}
  24EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
  25
  26/**
  27 * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
  28 *                               format expected by the SMC engine
  29 * @ncycles: number of MCK clk cycles
  30 * @msbpos: position of the MSB part of the timing field
  31 * @msbwidth: width of the MSB part of the timing field
  32 * @msbfactor: factor applied to the MSB
  33 * @encodedval: param used to store the encoding result
  34 *
  35 * This function encodes the @ncycles value as described in the datasheet
  36 * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
  37 * helper which called with different parameter depending on the encoding
  38 * scheme.
  39 *
  40 * If the @ncycles value is too big to be encoded, -ERANGE is returned and
  41 * the encodedval is contains the maximum val. Otherwise, 0 is returned.
  42 */
  43static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
  44                                       unsigned int msbpos,
  45                                       unsigned int msbwidth,
  46                                       unsigned int msbfactor,
  47                                       unsigned int *encodedval)
  48{
  49        unsigned int lsbmask = GENMASK(msbpos - 1, 0);
  50        unsigned int msbmask = GENMASK(msbwidth - 1, 0);
  51        unsigned int msb, lsb;
  52        int ret = 0;
  53
  54        msb = ncycles / msbfactor;
  55        lsb = ncycles % msbfactor;
  56
  57        if (lsb > lsbmask) {
  58                lsb = 0;
  59                msb++;
  60        }
  61
  62        /*
  63         * Let's just put the maximum we can if the requested setting does
  64         * not fit in the register field.
  65         * We still return -ERANGE in case the caller cares.
  66         */
  67        if (msb > msbmask) {
  68                msb = msbmask;
  69                lsb = lsbmask;
  70                ret = -ERANGE;
  71        }
  72
  73        *encodedval = (msb << msbpos) | lsb;
  74
  75        return ret;
  76}
  77
  78/**
  79 * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
  80 *                                specific value
  81 * @conf: SMC CS conf descriptor
  82 * @shift: the position of the Txx field in the TIMINGS register
  83 * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
  84 *           parameter
  85 *
  86 * This function encodes the @ncycles value as described in the datasheet
  87 * (section "SMC Timings Register"), and then stores the result in the
  88 * @conf->timings field at @shift position.
  89 *
  90 * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
  91 * the field, and 0 otherwise.
  92 */
  93int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
  94                                 unsigned int shift, unsigned int ncycles)
  95{
  96        unsigned int val;
  97        int ret;
  98
  99        if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
 100            shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
 101            shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
 102            shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
 103            shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
 104                return -EINVAL;
 105
 106        /*
 107         * The formula described in atmel datasheets (section "HSMC Timings
 108         * Register"):
 109         *
 110         * ncycles = (Txx[3] * 64) + Txx[2:0]
 111         */
 112        ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
 113        conf->timings &= ~GENMASK(shift + 3, shift);
 114        conf->timings |= val << shift;
 115
 116        return ret;
 117}
 118EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
 119
 120/**
 121 * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
 122 *                               specific value
 123 * @conf: SMC CS conf descriptor
 124 * @shift: the position of the xx_SETUP field in the SETUP register
 125 * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
 126 *           parameter
 127 *
 128 * This function encodes the @ncycles value as described in the datasheet
 129 * (section "SMC Setup Register"), and then stores the result in the
 130 * @conf->setup field at @shift position.
 131 *
 132 * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
 133 * the field, and 0 otherwise.
 134 */
 135int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
 136                                unsigned int shift, unsigned int ncycles)
 137{
 138        unsigned int val;
 139        int ret;
 140
 141        if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
 142            shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
 143                return -EINVAL;
 144
 145        /*
 146         * The formula described in atmel datasheets (section "SMC Setup
 147         * Register"):
 148         *
 149         * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
 150         */
 151        ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
 152        conf->setup &= ~GENMASK(shift + 7, shift);
 153        conf->setup |= val << shift;
 154
 155        return ret;
 156}
 157EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
 158
 159/**
 160 * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
 161 *                               specific value
 162 * @conf: SMC CS conf descriptor
 163 * @shift: the position of the xx_PULSE field in the PULSE register
 164 * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
 165 *           parameter
 166 *
 167 * This function encodes the @ncycles value as described in the datasheet
 168 * (section "SMC Pulse Register"), and then stores the result in the
 169 * @conf->setup field at @shift position.
 170 *
 171 * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
 172 * the field, and 0 otherwise.
 173 */
 174int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
 175                                unsigned int shift, unsigned int ncycles)
 176{
 177        unsigned int val;
 178        int ret;
 179
 180        if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
 181            shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
 182                return -EINVAL;
 183
 184        /*
 185         * The formula described in atmel datasheets (section "SMC Pulse
 186         * Register"):
 187         *
 188         * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
 189         */
 190        ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
 191        conf->pulse &= ~GENMASK(shift + 7, shift);
 192        conf->pulse |= val << shift;
 193
 194        return ret;
 195}
 196EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
 197
 198/**
 199 * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
 200 *                               specific value
 201 * @conf: SMC CS conf descriptor
 202 * @shift: the position of the xx_CYCLE field in the CYCLE register
 203 * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
 204 *           parameter
 205 *
 206 * This function encodes the @ncycles value as described in the datasheet
 207 * (section "SMC Cycle Register"), and then stores the result in the
 208 * @conf->setup field at @shift position.
 209 *
 210 * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
 211 * the field, and 0 otherwise.
 212 */
 213int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
 214                                unsigned int shift, unsigned int ncycles)
 215{
 216        unsigned int val;
 217        int ret;
 218
 219        if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
 220                return -EINVAL;
 221
 222        /*
 223         * The formula described in atmel datasheets (section "SMC Cycle
 224         * Register"):
 225         *
 226         * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
 227         */
 228        ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
 229        conf->cycle &= ~GENMASK(shift + 15, shift);
 230        conf->cycle |= val << shift;
 231
 232        return ret;
 233}
 234EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
 235
 236/**
 237 * atmel_smc_cs_conf_apply - apply an SMC CS conf
 238 * @regmap: the SMC regmap
 239 * @cs: the CS id
 240 * @conf: the SMC CS conf to apply
 241 *
 242 * Applies an SMC CS configuration.
 243 * Only valid on at91sam9/avr32 SoCs.
 244 */
 245void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
 246                             const struct atmel_smc_cs_conf *conf)
 247{
 248        regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
 249        regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
 250        regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
 251        regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
 252}
 253EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
 254
 255/**
 256 * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
 257 * @regmap: the HSMC regmap
 258 * @cs: the CS id
 259 * @layout: the layout of registers
 260 * @conf: the SMC CS conf to apply
 261 *
 262 * Applies an SMC CS configuration.
 263 * Only valid on post-sama5 SoCs.
 264 */
 265void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
 266                              const struct atmel_hsmc_reg_layout *layout,
 267                              int cs, const struct atmel_smc_cs_conf *conf)
 268{
 269        regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
 270        regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
 271        regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
 272        regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
 273        regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
 274}
 275EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
 276
 277/**
 278 * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
 279 * @regmap: the SMC regmap
 280 * @cs: the CS id
 281 * @conf: the SMC CS conf object to store the current conf
 282 *
 283 * Retrieve the SMC CS configuration.
 284 * Only valid on at91sam9/avr32 SoCs.
 285 */
 286void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
 287                           struct atmel_smc_cs_conf *conf)
 288{
 289        regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
 290        regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
 291        regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
 292        regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
 293}
 294EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
 295
 296/**
 297 * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
 298 * @regmap: the HSMC regmap
 299 * @cs: the CS id
 300 * @layout: the layout of registers
 301 * @conf: the SMC CS conf object to store the current conf
 302 *
 303 * Retrieve the SMC CS configuration.
 304 * Only valid on post-sama5 SoCs.
 305 */
 306void atmel_hsmc_cs_conf_get(struct regmap *regmap,
 307                            const struct atmel_hsmc_reg_layout *layout,
 308                            int cs, struct atmel_smc_cs_conf *conf)
 309{
 310        regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
 311        regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
 312        regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
 313        regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
 314        regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
 315}
 316EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
 317
 318static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
 319        .timing_regs_offset = 0x600,
 320};
 321
 322static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
 323        .timing_regs_offset = 0x700,
 324};
 325
 326static const struct of_device_id atmel_smc_ids[] = {
 327        { .compatible = "atmel,at91sam9260-smc", .data = NULL },
 328        { .compatible = "atmel,sama5d3-smc", .data = &sama5d3_reg_layout },
 329        { .compatible = "atmel,sama5d2-smc", .data = &sama5d2_reg_layout },
 330        { /* sentinel */ },
 331};
 332
 333/**
 334 * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
 335 * @np: the HSMC regmap
 336 *
 337 * Retrieve the layout of HSMC registers.
 338 *
 339 * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
 340 * in HSMC case, otherwise ERR_PTR(-EINVAL).
 341 */
 342const struct atmel_hsmc_reg_layout *
 343atmel_hsmc_get_reg_layout(struct device_node *np)
 344{
 345        const struct of_device_id *match;
 346
 347        match = of_match_node(atmel_smc_ids, np);
 348
 349        return match ? match->data : ERR_PTR(-EINVAL);
 350}
 351EXPORT_SYMBOL_GPL(atmel_hsmc_get_reg_layout);
 352