/* * Marvell Orion SPI controller driver * * Author: Shadi Ammouri <shadi@marvell.com> * Copyright (C) 2007-2008 Marvell Ltd. * * 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/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/spi/spi.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/clk.h> #include <linux/sizes.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" /* Runtime PM autosuspend timeout: PM is fairly light on this driver */ #define SPI_AUTOSUSPEND_TIMEOUT 200 /* Some SoCs using this driver support up to 8 chip selects. * It is up to the implementer to only use the chip selects * that are available. */ #define ORION_NUM_CHIPSELECTS 8 #define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ #define ORION_SPI_IF_CTRL_REG 0x00 #define ORION_SPI_IF_CONFIG_REG 0x04 #define ORION_SPI_DATA_OUT_REG 0x08 #define ORION_SPI_DATA_IN_REG 0x0c #define ORION_SPI_INT_CAUSE_REG 0x10 #define ORION_SPI_TIMING_PARAMS_REG 0x18 #define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6) #define ORION_SPI_TMISO_SAMPLE_1 (1 << 6) #define ORION_SPI_TMISO_SAMPLE_2 (2 << 6) #define ORION_SPI_MODE_CPOL (1 << 11) #define ORION_SPI_MODE_CPHA (1 << 12) #define ORION_SPI_IF_8_16_BIT_MODE (1 << 5) #define ORION_SPI_CLK_PRESCALE_MASK 0x1F #define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF #define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \ ORION_SPI_MODE_CPHA) #define ORION_SPI_CS_MASK 0x1C #define ORION_SPI_CS_SHIFT 2 #define ORION_SPI_CS(cs) ((cs << ORION_SPI_CS_SHIFT) & \ ORION_SPI_CS_MASK) enum orion_spi_type { ORION_SPI, ARMADA_SPI, }; struct orion_spi_dev { enum orion_spi_type typ; /* * min_divisor and max_hz should be exclusive, the only we can * have both is for managing the armada-370-spi case with old * device tree */ unsigned long max_hz; unsigned int min_divisor; unsigned int max_divisor; u32 prescale_mask; bool is_errata_50mhz_ac; }; struct orion_spi { struct spi_master *master; void __iomem *base; struct clk *clk; const struct orion_spi_dev *devdata; }; static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) { return orion_spi->base + reg; } static inline void orion_spi_setbits(struct orion_spi *orion_spi, u32 reg, u32 mask) { void __iomem *reg_addr = spi_reg(orion_spi, reg); u32 val; val = readl(reg_addr); val |= mask; writel(val, reg_addr); } static inline void orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask) { void __iomem *reg_addr = spi_reg(orion_spi, reg); u32 val; val = readl(reg_addr); val &= ~mask; writel(val, reg_addr); } static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) { u32 tclk_hz; u32 rate; u32 prescale; u32 reg; struct orion_spi *orion_spi; const struct orion_spi_dev *devdata; orion_spi = spi_master_get_devdata(spi->master); devdata = orion_spi->devdata; tclk_hz = clk_get_rate(orion_spi->clk); if (devdata->typ == ARMADA_SPI) { unsigned int clk, spr, sppr, sppr2, err; unsigned int best_spr, best_sppr, best_err; best_err = speed; best_spr = 0; best_sppr = 0; /* Iterate over the valid range looking for best fit */ for (sppr = 0; sppr < 8; sppr++) { sppr2 = 0x1 << sppr; spr = tclk_hz / sppr2; spr = DIV_ROUND_UP(spr, speed); if ((spr == 0) || (spr > 15)) continue; clk = tclk_hz / (spr * sppr2); err = speed - clk; if (err < best_err) { best_spr = spr; best_sppr = sppr; best_err = err; } } if ((best_sppr == 0) && (best_spr == 0)) return -EINVAL; prescale = ((best_sppr & 0x6) << 5) | ((best_sppr & 0x1) << 4) | best_spr; } else { /* * the supported rates are: 4,6,8...30 * round up as we look for equal or less speed */ rate = DIV_ROUND_UP(tclk_hz, speed); rate = roundup(rate, 2); /* check if requested speed is too small */ if (rate > 30) return -EINVAL; if (rate < 4) rate = 4; /* Convert the rate to SPI clock divisor value. */ prescale = 0x10 + rate/2; } reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); reg = ((reg & ~devdata->prescale_mask) | prescale); writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); return 0; } static void orion_spi_mode_set(struct spi_device *spi) { u32 reg; struct orion_spi *orion_spi; orion_spi = spi_master_get_devdata(spi->master); reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); reg &= ~ORION_SPI_MODE_MASK; if (spi->mode & SPI_CPOL) reg |= ORION_SPI_MODE_CPOL; if (spi->mode & SPI_CPHA) reg |= ORION_SPI_MODE_CPHA; writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); } static void orion_spi_50mhz_ac_timing_erratum(struct spi_device *spi, unsigned int speed) { u32 reg; struct orion_spi *orion_spi; orion_spi = spi_master_get_devdata(spi->master); /* * Erratum description: (Erratum NO. FE-9144572) The device * SPI interface supports frequencies of up to 50 MHz. * However, due to this erratum, when the device core clock is * 250 MHz and the SPI interfaces is configured for 50MHz SPI * clock and CPOL=CPHA=1 there might occur data corruption on * reads from the SPI device. * Erratum Workaround: * Work in one of the following configurations: * 1. Set CPOL=CPHA=0 in "SPI Interface Configuration * Register". * 2. Set TMISO_SAMPLE value to 0x2 in "SPI Timing Parameters 1 * Register" before setting the interface. */ reg = readl(spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG)); reg &= ~ORION_SPI_TMISO_SAMPLE_MASK; if (clk_get_rate(orion_spi->clk) == 250000000 && speed == 50000000 && spi->mode & SPI_CPOL && spi->mode & SPI_CPHA) reg |= ORION_SPI_TMISO_SAMPLE_2; else reg |= ORION_SPI_TMISO_SAMPLE_1; /* This is the default value */ writel(reg, spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG)); } /* * called only when no transfer is active on the bus */ static int orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct orion_spi *orion_spi; unsigned int speed = spi->max_speed_hz; unsigned int bits_per_word = spi->bits_per_word; int rc; orion_spi = spi_master_get_devdata(spi->master); if ((t != NULL) && t->speed_hz) speed = t->speed_hz; if ((t != NULL) && t->bits_per_word) bits_per_word = t->bits_per_word; orion_spi_mode_set(spi); if (o<style>.highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span>Kernel driver abituguru ======================= Supported chips: * Abit uGuru revision 1 & 2 (Hardware Monitor part only) Prefix: 'abituguru' Addresses scanned: ISA 0x0E0 Datasheet: Not available, this driver is based on reverse engineering. A "Datasheet" has been written based on the reverse engineering it should be available in the same dir as this file under the name abituguru-datasheet. Note: The uGuru is a microcontroller with onboard firmware which programs it to behave as a hwmon IC. There are many different revisions of the firmware and thus effectivly many different revisions of the uGuru. Below is an incomplete list with which revisions are used for which Motherboards: uGuru 1.00 ~ 1.24 (AI7, KV8-MAX3, AN7) (1) uGuru 2.0.0.0 ~ 2.0.4.2 (KV8-PRO) uGuru 2.1.0.0 ~ 2.1.2.8 (AS8, AV8, AA8, AG8, AA8XE, AX8) uGuru 2.2.0.0 ~ 2.2.0.6 (AA8 Fatal1ty) uGuru 2.3.0.0 ~ 2.3.0.9 (AN8) uGuru 3.0.0.0 ~ 3.0.x.x (AW8, AL8, AT8, NI8 SLI, AT8 32X, AN8 32X, AW9D-MAX) (2) 1) For revisions 2 and 3 uGuru's the driver can autodetect the sensortype (Volt or Temp) for bank1 sensors, for revision 1 uGuru's this doesnot always work. For these uGuru's the autodection can be overriden with the bank1_types module param. For all 3 known revison 1 motherboards the correct use of this param is: bank1_types=1,1,0,0,0,0,0,2,0,0,0,0,2,0,0,1 You may also need to specify the fan_sensors option for these boards fan_sensors=5 2) There is a separate abituguru3 driver for these motherboards, the abituguru (without the 3 !) driver will not work on these motherboards (and visa versa)! Authors: Hans de Goede <j.w.r.degoede@hhs.nl>, (Initial reverse engineering done by Olle Sandberg <ollebull@gmail.com>) Module Parameters ----------------- * force: bool Force detection. Note this parameter only causes the detection to be skipped, and thus the insmod to succeed. If the uGuru can't be read the actual hwmon driver will not load and thus no hwmon device will get registered. * bank1_types: int[] Bank1 sensortype autodetection override: -1 autodetect (default) 0 volt sensor 1 temp sensor 2 not connected * fan_sensors: int Tell the driver how many fan speed sensors there are on your motherboard. Default: 0 (autodetect). * pwms: int Tell the driver how many fan speed controls (fan pwms) your motherboard has. Default: 0 (autodetect). * verbose: int How verbose should the driver be? (0-3): 0 normal output 1 + verbose error reporting 2 + sensors type probing info (default) 3 + retryable error reporting Default: 2 (the driver is still in the testing phase) Notice if you need any of the first three options above please insmod the driver with verbose set to 3 and mail me <j.w.r.degoede@hhs.nl> the output of: dmesg | grep abituguru Description ----------- This driver supports the hardware monitoring features of the first and second revision of the Abit uGuru chip found on Abit uGuru featuring motherboards (most modern Abit motherboards). The first and second revision of the uGuru chip in reality is a Winbond W83L950D in disguise (despite Abit claiming it is "a new microprocessor designed by the ABIT Engineers"). Unfortunately this doesn't help since the W83L950D is a generic microcontroller with a custom Abit application running on it. Despite Abit not releasing any information regarding the uGuru, Olle Sandberg <ollebull@gmail.com> has managed to reverse engineer the sensor part of the uGuru. Without his work this driver would not have been possible. Known Issues ------------ The voltage and frequency control parts of the Abit uGuru are not supported. </pre></div> </code></pre></td></tr></table> </div> <!-- class=content --> <div id="lfcollabprojects-footer"> <div class="gray-diagonal"> <div class="footer-inner"> <p> © 2015 <a href="https://opnfv.org/">Open Platform for NFV Project, Inc</a>., a Linux Foundation Collaborative Project. All Rights Reserved. </p> <p> Open Platform for NFV and OPNFV are trademarks of the Open Platform for NFV Project, Inc. </p> <p> Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered <a href="http://www.linuxfoundation.org/programs/legal/trademark" title="Linux Mark Institute" >trademark</a > of Linus Torvalds. </p> <p> Please see our <a href="https://opnfv.org/about/bylaws-and-policies/terms-use" >terms of use</a >, <a href="https://opnfv.org/about/bylaws-and-