162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Driver for Amlogic Meson SPI communication controller (SPICC) 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) BayLibre, SAS 562306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * SPDX-License-Identifier: GPL-2.0+ 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/clk-provider.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/spi/spi.h> 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/reset.h> 2362306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * The Meson SPICC controller could support DMA based transfers, but is not 2762306a36Sopenharmony_ci * implemented by the vendor code, and while having the registers documentation 2862306a36Sopenharmony_ci * it has never worked on the GXL Hardware. 2962306a36Sopenharmony_ci * The PIO mode is the only mode implemented, and due to badly designed HW : 3062306a36Sopenharmony_ci * - all transfers are cutted in 16 words burst because the FIFO hangs on 3162306a36Sopenharmony_ci * TX underflow, and there is no TX "Half-Empty" interrupt, so we go by 3262306a36Sopenharmony_ci * FIFO max size chunk only 3362306a36Sopenharmony_ci * - CS management is dumb, and goes UP between every burst, so is really a 3462306a36Sopenharmony_ci * "Data Valid" signal than a Chip Select, GPIO link should be used instead 3562306a36Sopenharmony_ci * to have a CS go down over the full transfer 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define SPICC_MAX_BURST 128 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Register Map */ 4162306a36Sopenharmony_ci#define SPICC_RXDATA 0x00 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define SPICC_TXDATA 0x04 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define SPICC_CONREG 0x08 4662306a36Sopenharmony_ci#define SPICC_ENABLE BIT(0) 4762306a36Sopenharmony_ci#define SPICC_MODE_MASTER BIT(1) 4862306a36Sopenharmony_ci#define SPICC_XCH BIT(2) 4962306a36Sopenharmony_ci#define SPICC_SMC BIT(3) 5062306a36Sopenharmony_ci#define SPICC_POL BIT(4) 5162306a36Sopenharmony_ci#define SPICC_PHA BIT(5) 5262306a36Sopenharmony_ci#define SPICC_SSCTL BIT(6) 5362306a36Sopenharmony_ci#define SPICC_SSPOL BIT(7) 5462306a36Sopenharmony_ci#define SPICC_DRCTL_MASK GENMASK(9, 8) 5562306a36Sopenharmony_ci#define SPICC_DRCTL_IGNORE 0 5662306a36Sopenharmony_ci#define SPICC_DRCTL_FALLING 1 5762306a36Sopenharmony_ci#define SPICC_DRCTL_LOWLEVEL 2 5862306a36Sopenharmony_ci#define SPICC_CS_MASK GENMASK(13, 12) 5962306a36Sopenharmony_ci#define SPICC_DATARATE_MASK GENMASK(18, 16) 6062306a36Sopenharmony_ci#define SPICC_DATARATE_DIV4 0 6162306a36Sopenharmony_ci#define SPICC_DATARATE_DIV8 1 6262306a36Sopenharmony_ci#define SPICC_DATARATE_DIV16 2 6362306a36Sopenharmony_ci#define SPICC_DATARATE_DIV32 3 6462306a36Sopenharmony_ci#define SPICC_BITLENGTH_MASK GENMASK(24, 19) 6562306a36Sopenharmony_ci#define SPICC_BURSTLENGTH_MASK GENMASK(31, 25) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define SPICC_INTREG 0x0c 6862306a36Sopenharmony_ci#define SPICC_TE_EN BIT(0) /* TX FIFO Empty Interrupt */ 6962306a36Sopenharmony_ci#define SPICC_TH_EN BIT(1) /* TX FIFO Half-Full Interrupt */ 7062306a36Sopenharmony_ci#define SPICC_TF_EN BIT(2) /* TX FIFO Full Interrupt */ 7162306a36Sopenharmony_ci#define SPICC_RR_EN BIT(3) /* RX FIFO Ready Interrupt */ 7262306a36Sopenharmony_ci#define SPICC_RH_EN BIT(4) /* RX FIFO Half-Full Interrupt */ 7362306a36Sopenharmony_ci#define SPICC_RF_EN BIT(5) /* RX FIFO Full Interrupt */ 7462306a36Sopenharmony_ci#define SPICC_RO_EN BIT(6) /* RX FIFO Overflow Interrupt */ 7562306a36Sopenharmony_ci#define SPICC_TC_EN BIT(7) /* Transfert Complete Interrupt */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define SPICC_DMAREG 0x10 7862306a36Sopenharmony_ci#define SPICC_DMA_ENABLE BIT(0) 7962306a36Sopenharmony_ci#define SPICC_TXFIFO_THRESHOLD_MASK GENMASK(5, 1) 8062306a36Sopenharmony_ci#define SPICC_RXFIFO_THRESHOLD_MASK GENMASK(10, 6) 8162306a36Sopenharmony_ci#define SPICC_READ_BURST_MASK GENMASK(14, 11) 8262306a36Sopenharmony_ci#define SPICC_WRITE_BURST_MASK GENMASK(18, 15) 8362306a36Sopenharmony_ci#define SPICC_DMA_URGENT BIT(19) 8462306a36Sopenharmony_ci#define SPICC_DMA_THREADID_MASK GENMASK(25, 20) 8562306a36Sopenharmony_ci#define SPICC_DMA_BURSTNUM_MASK GENMASK(31, 26) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define SPICC_STATREG 0x14 8862306a36Sopenharmony_ci#define SPICC_TE BIT(0) /* TX FIFO Empty Interrupt */ 8962306a36Sopenharmony_ci#define SPICC_TH BIT(1) /* TX FIFO Half-Full Interrupt */ 9062306a36Sopenharmony_ci#define SPICC_TF BIT(2) /* TX FIFO Full Interrupt */ 9162306a36Sopenharmony_ci#define SPICC_RR BIT(3) /* RX FIFO Ready Interrupt */ 9262306a36Sopenharmony_ci#define SPICC_RH BIT(4) /* RX FIFO Half-Full Interrupt */ 9362306a36Sopenharmony_ci#define SPICC_RF BIT(5) /* RX FIFO Full Interrupt */ 9462306a36Sopenharmony_ci#define SPICC_RO BIT(6) /* RX FIFO Overflow Interrupt */ 9562306a36Sopenharmony_ci#define SPICC_TC BIT(7) /* Transfert Complete Interrupt */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define SPICC_PERIODREG 0x18 9862306a36Sopenharmony_ci#define SPICC_PERIOD GENMASK(14, 0) /* Wait cycles */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define SPICC_TESTREG 0x1c 10162306a36Sopenharmony_ci#define SPICC_TXCNT_MASK GENMASK(4, 0) /* TX FIFO Counter */ 10262306a36Sopenharmony_ci#define SPICC_RXCNT_MASK GENMASK(9, 5) /* RX FIFO Counter */ 10362306a36Sopenharmony_ci#define SPICC_SMSTATUS_MASK GENMASK(12, 10) /* State Machine Status */ 10462306a36Sopenharmony_ci#define SPICC_LBC_RO BIT(13) /* Loop Back Control Read-Only */ 10562306a36Sopenharmony_ci#define SPICC_LBC_W1 BIT(14) /* Loop Back Control Write-Only */ 10662306a36Sopenharmony_ci#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */ 10762306a36Sopenharmony_ci#define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */ 10862306a36Sopenharmony_ci#define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */ 10962306a36Sopenharmony_ci#define SPICC_MO_DELAY_MASK GENMASK(17, 16) /* Master Output Delay */ 11062306a36Sopenharmony_ci#define SPICC_MO_NO_DELAY 0 11162306a36Sopenharmony_ci#define SPICC_MO_DELAY_1_CYCLE 1 11262306a36Sopenharmony_ci#define SPICC_MO_DELAY_2_CYCLE 2 11362306a36Sopenharmony_ci#define SPICC_MO_DELAY_3_CYCLE 3 11462306a36Sopenharmony_ci#define SPICC_MI_DELAY_MASK GENMASK(19, 18) /* Master Input Delay */ 11562306a36Sopenharmony_ci#define SPICC_MI_NO_DELAY 0 11662306a36Sopenharmony_ci#define SPICC_MI_DELAY_1_CYCLE 1 11762306a36Sopenharmony_ci#define SPICC_MI_DELAY_2_CYCLE 2 11862306a36Sopenharmony_ci#define SPICC_MI_DELAY_3_CYCLE 3 11962306a36Sopenharmony_ci#define SPICC_MI_CAP_DELAY_MASK GENMASK(21, 20) /* Master Capture Delay */ 12062306a36Sopenharmony_ci#define SPICC_CAP_AHEAD_2_CYCLE 0 12162306a36Sopenharmony_ci#define SPICC_CAP_AHEAD_1_CYCLE 1 12262306a36Sopenharmony_ci#define SPICC_CAP_NO_DELAY 2 12362306a36Sopenharmony_ci#define SPICC_CAP_DELAY_1_CYCLE 3 12462306a36Sopenharmony_ci#define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */ 12562306a36Sopenharmony_ci#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define SPICC_DRADDR 0x20 /* Read Address of DMA */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define SPICC_DWADDR 0x24 /* Write Address of DMA */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define SPICC_ENH_CTL0 0x38 /* Enhanced Feature */ 13262306a36Sopenharmony_ci#define SPICC_ENH_CLK_CS_DELAY_MASK GENMASK(15, 0) 13362306a36Sopenharmony_ci#define SPICC_ENH_DATARATE_MASK GENMASK(23, 16) 13462306a36Sopenharmony_ci#define SPICC_ENH_DATARATE_EN BIT(24) 13562306a36Sopenharmony_ci#define SPICC_ENH_MOSI_OEN BIT(25) 13662306a36Sopenharmony_ci#define SPICC_ENH_CLK_OEN BIT(26) 13762306a36Sopenharmony_ci#define SPICC_ENH_CS_OEN BIT(27) 13862306a36Sopenharmony_ci#define SPICC_ENH_CLK_CS_DELAY_EN BIT(28) 13962306a36Sopenharmony_ci#define SPICC_ENH_MAIN_CLK_AO BIT(29) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define writel_bits_relaxed(mask, val, addr) \ 14262306a36Sopenharmony_ci writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct meson_spicc_data { 14562306a36Sopenharmony_ci unsigned int max_speed_hz; 14662306a36Sopenharmony_ci unsigned int min_speed_hz; 14762306a36Sopenharmony_ci unsigned int fifo_size; 14862306a36Sopenharmony_ci bool has_oen; 14962306a36Sopenharmony_ci bool has_enhance_clk_div; 15062306a36Sopenharmony_ci bool has_pclk; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct meson_spicc_device { 15462306a36Sopenharmony_ci struct spi_master *master; 15562306a36Sopenharmony_ci struct platform_device *pdev; 15662306a36Sopenharmony_ci void __iomem *base; 15762306a36Sopenharmony_ci struct clk *core; 15862306a36Sopenharmony_ci struct clk *pclk; 15962306a36Sopenharmony_ci struct clk_divider pow2_div; 16062306a36Sopenharmony_ci struct clk *clk; 16162306a36Sopenharmony_ci struct spi_message *message; 16262306a36Sopenharmony_ci struct spi_transfer *xfer; 16362306a36Sopenharmony_ci struct completion done; 16462306a36Sopenharmony_ci const struct meson_spicc_data *data; 16562306a36Sopenharmony_ci u8 *tx_buf; 16662306a36Sopenharmony_ci u8 *rx_buf; 16762306a36Sopenharmony_ci unsigned int bytes_per_word; 16862306a36Sopenharmony_ci unsigned long tx_remain; 16962306a36Sopenharmony_ci unsigned long rx_remain; 17062306a36Sopenharmony_ci unsigned long xfer_remain; 17162306a36Sopenharmony_ci struct pinctrl *pinctrl; 17262306a36Sopenharmony_ci struct pinctrl_state *pins_idle_high; 17362306a36Sopenharmony_ci struct pinctrl_state *pins_idle_low; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div) 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void meson_spicc_oen_enable(struct meson_spicc_device *spicc) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci u32 conf; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!spicc->data->has_oen) { 18362306a36Sopenharmony_ci /* Try to get pinctrl states for idle high/low */ 18462306a36Sopenharmony_ci spicc->pins_idle_high = pinctrl_lookup_state(spicc->pinctrl, 18562306a36Sopenharmony_ci "idle-high"); 18662306a36Sopenharmony_ci if (IS_ERR(spicc->pins_idle_high)) { 18762306a36Sopenharmony_ci dev_warn(&spicc->pdev->dev, "can't get idle-high pinctrl\n"); 18862306a36Sopenharmony_ci spicc->pins_idle_high = NULL; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci spicc->pins_idle_low = pinctrl_lookup_state(spicc->pinctrl, 19162306a36Sopenharmony_ci "idle-low"); 19262306a36Sopenharmony_ci if (IS_ERR(spicc->pins_idle_low)) { 19362306a36Sopenharmony_ci dev_warn(&spicc->pdev->dev, "can't get idle-low pinctrl\n"); 19462306a36Sopenharmony_ci spicc->pins_idle_low = NULL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) | 20062306a36Sopenharmony_ci SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci return !!FIELD_GET(SPICC_TF, 20862306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_STATREG)); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic inline bool meson_spicc_rxready(struct meson_spicc_device *spicc) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF, 21462306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_STATREG)); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci unsigned int bytes = spicc->bytes_per_word; 22062306a36Sopenharmony_ci unsigned int byte_shift = 0; 22162306a36Sopenharmony_ci u32 data = 0; 22262306a36Sopenharmony_ci u8 byte; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci while (bytes--) { 22562306a36Sopenharmony_ci byte = *spicc->tx_buf++; 22662306a36Sopenharmony_ci data |= (byte & 0xff) << byte_shift; 22762306a36Sopenharmony_ci byte_shift += 8; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci spicc->tx_remain--; 23162306a36Sopenharmony_ci return data; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline void meson_spicc_push_data(struct meson_spicc_device *spicc, 23562306a36Sopenharmony_ci u32 data) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci unsigned int bytes = spicc->bytes_per_word; 23862306a36Sopenharmony_ci unsigned int byte_shift = 0; 23962306a36Sopenharmony_ci u8 byte; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci while (bytes--) { 24262306a36Sopenharmony_ci byte = (data >> byte_shift) & 0xff; 24362306a36Sopenharmony_ci *spicc->rx_buf++ = byte; 24462306a36Sopenharmony_ci byte_shift += 8; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spicc->rx_remain--; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline void meson_spicc_rx(struct meson_spicc_device *spicc) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci /* Empty RX FIFO */ 25362306a36Sopenharmony_ci while (spicc->rx_remain && 25462306a36Sopenharmony_ci meson_spicc_rxready(spicc)) 25562306a36Sopenharmony_ci meson_spicc_push_data(spicc, 25662306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_RXDATA)); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic inline void meson_spicc_tx(struct meson_spicc_device *spicc) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci /* Fill Up TX FIFO */ 26262306a36Sopenharmony_ci while (spicc->tx_remain && 26362306a36Sopenharmony_ci !meson_spicc_txfull(spicc)) 26462306a36Sopenharmony_ci writel_relaxed(meson_spicc_pull_data(spicc), 26562306a36Sopenharmony_ci spicc->base + SPICC_TXDATA); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci unsigned int burst_len = min_t(unsigned int, 27262306a36Sopenharmony_ci spicc->xfer_remain / 27362306a36Sopenharmony_ci spicc->bytes_per_word, 27462306a36Sopenharmony_ci spicc->data->fifo_size); 27562306a36Sopenharmony_ci /* Setup Xfer variables */ 27662306a36Sopenharmony_ci spicc->tx_remain = burst_len; 27762306a36Sopenharmony_ci spicc->rx_remain = burst_len; 27862306a36Sopenharmony_ci spicc->xfer_remain -= burst_len * spicc->bytes_per_word; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Setup burst length */ 28162306a36Sopenharmony_ci writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, 28262306a36Sopenharmony_ci FIELD_PREP(SPICC_BURSTLENGTH_MASK, 28362306a36Sopenharmony_ci burst_len - 1), 28462306a36Sopenharmony_ci spicc->base + SPICC_CONREG); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Fill TX FIFO */ 28762306a36Sopenharmony_ci meson_spicc_tx(spicc); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic irqreturn_t meson_spicc_irq(int irq, void *data) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct meson_spicc_device *spicc = (void *) data; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Empty RX FIFO */ 29762306a36Sopenharmony_ci meson_spicc_rx(spicc); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!spicc->xfer_remain) { 30062306a36Sopenharmony_ci /* Disable all IRQs */ 30162306a36Sopenharmony_ci writel(0, spicc->base + SPICC_INTREG); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci complete(&spicc->done); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return IRQ_HANDLED; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Setup burst */ 30962306a36Sopenharmony_ci meson_spicc_setup_burst(spicc); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Start burst */ 31262306a36Sopenharmony_ci writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return IRQ_HANDLED; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci u32 div, hz; 32062306a36Sopenharmony_ci u32 mi_delay, cap_delay; 32162306a36Sopenharmony_ci u32 conf; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (spicc->data->has_enhance_clk_div) { 32462306a36Sopenharmony_ci div = FIELD_GET(SPICC_ENH_DATARATE_MASK, 32562306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_ENH_CTL0)); 32662306a36Sopenharmony_ci div++; 32762306a36Sopenharmony_ci div <<= 1; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci div = FIELD_GET(SPICC_DATARATE_MASK, 33062306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_CONREG)); 33162306a36Sopenharmony_ci div += 2; 33262306a36Sopenharmony_ci div = 1 << div; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci mi_delay = SPICC_MI_NO_DELAY; 33662306a36Sopenharmony_ci cap_delay = SPICC_CAP_AHEAD_2_CYCLE; 33762306a36Sopenharmony_ci hz = clk_get_rate(spicc->clk); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (hz >= 100000000) 34062306a36Sopenharmony_ci cap_delay = SPICC_CAP_DELAY_1_CYCLE; 34162306a36Sopenharmony_ci else if (hz >= 80000000) 34262306a36Sopenharmony_ci cap_delay = SPICC_CAP_NO_DELAY; 34362306a36Sopenharmony_ci else if (hz >= 40000000) 34462306a36Sopenharmony_ci cap_delay = SPICC_CAP_AHEAD_1_CYCLE; 34562306a36Sopenharmony_ci else if (div >= 16) 34662306a36Sopenharmony_ci mi_delay = SPICC_MI_DELAY_3_CYCLE; 34762306a36Sopenharmony_ci else if (div >= 8) 34862306a36Sopenharmony_ci mi_delay = SPICC_MI_DELAY_2_CYCLE; 34962306a36Sopenharmony_ci else if (div >= 6) 35062306a36Sopenharmony_ci mi_delay = SPICC_MI_DELAY_1_CYCLE; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci conf = readl_relaxed(spicc->base + SPICC_TESTREG); 35362306a36Sopenharmony_ci conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK 35462306a36Sopenharmony_ci | SPICC_MI_CAP_DELAY_MASK); 35562306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay); 35662306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay); 35762306a36Sopenharmony_ci writel_relaxed(conf, spicc->base + SPICC_TESTREG); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, 36162306a36Sopenharmony_ci struct spi_transfer *xfer) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci u32 conf, conf_orig; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Read original configuration */ 36662306a36Sopenharmony_ci conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Setup word width */ 36962306a36Sopenharmony_ci conf &= ~SPICC_BITLENGTH_MASK; 37062306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 37162306a36Sopenharmony_ci (spicc->bytes_per_word << 3) - 1); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Ignore if unchanged */ 37462306a36Sopenharmony_ci if (conf != conf_orig) 37562306a36Sopenharmony_ci writel_relaxed(conf, spicc->base + SPICC_CONREG); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci clk_set_rate(spicc->clk, xfer->speed_hz); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci meson_spicc_auto_io_delay(spicc); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci writel_relaxed(0, spicc->base + SPICC_DMAREG); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void meson_spicc_reset_fifo(struct meson_spicc_device *spicc) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci if (spicc->data->has_oen) 38762306a36Sopenharmony_ci writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 38862306a36Sopenharmony_ci SPICC_ENH_MAIN_CLK_AO, 38962306a36Sopenharmony_ci spicc->base + SPICC_ENH_CTL0); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK, 39262306a36Sopenharmony_ci spicc->base + SPICC_TESTREG); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci while (meson_spicc_rxready(spicc)) 39562306a36Sopenharmony_ci readl_relaxed(spicc->base + SPICC_RXDATA); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (spicc->data->has_oen) 39862306a36Sopenharmony_ci writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0, 39962306a36Sopenharmony_ci spicc->base + SPICC_ENH_CTL0); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int meson_spicc_transfer_one(struct spi_master *master, 40362306a36Sopenharmony_ci struct spi_device *spi, 40462306a36Sopenharmony_ci struct spi_transfer *xfer) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct meson_spicc_device *spicc = spi_master_get_devdata(master); 40762306a36Sopenharmony_ci uint64_t timeout; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Store current transfer */ 41062306a36Sopenharmony_ci spicc->xfer = xfer; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Setup transfer parameters */ 41362306a36Sopenharmony_ci spicc->tx_buf = (u8 *)xfer->tx_buf; 41462306a36Sopenharmony_ci spicc->rx_buf = (u8 *)xfer->rx_buf; 41562306a36Sopenharmony_ci spicc->xfer_remain = xfer->len; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Pre-calculate word size */ 41862306a36Sopenharmony_ci spicc->bytes_per_word = 41962306a36Sopenharmony_ci DIV_ROUND_UP(spicc->xfer->bits_per_word, 8); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (xfer->len % spicc->bytes_per_word) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Setup transfer parameters */ 42562306a36Sopenharmony_ci meson_spicc_setup_xfer(spicc, xfer); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci meson_spicc_reset_fifo(spicc); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Setup burst */ 43062306a36Sopenharmony_ci meson_spicc_setup_burst(spicc); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Setup wait for completion */ 43362306a36Sopenharmony_ci reinit_completion(&spicc->done); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* For each byte we wait for 8 cycles of the SPI clock */ 43662306a36Sopenharmony_ci timeout = 8LL * MSEC_PER_SEC * xfer->len; 43762306a36Sopenharmony_ci do_div(timeout, xfer->speed_hz); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Add 10us delay between each fifo bursts */ 44062306a36Sopenharmony_ci timeout += ((xfer->len >> 4) * 10) / MSEC_PER_SEC; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Increase it twice and add 200 ms tolerance */ 44362306a36Sopenharmony_ci timeout += timeout + 200; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Start burst */ 44662306a36Sopenharmony_ci writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Enable interrupts */ 44962306a36Sopenharmony_ci writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout))) 45262306a36Sopenharmony_ci return -ETIMEDOUT; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int meson_spicc_prepare_message(struct spi_master *master, 45862306a36Sopenharmony_ci struct spi_message *message) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct meson_spicc_device *spicc = spi_master_get_devdata(master); 46162306a36Sopenharmony_ci struct spi_device *spi = message->spi; 46262306a36Sopenharmony_ci u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Store current message */ 46562306a36Sopenharmony_ci spicc->message = message; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Enable Master */ 46862306a36Sopenharmony_ci conf |= SPICC_ENABLE; 46962306a36Sopenharmony_ci conf |= SPICC_MODE_MASTER; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* SMC = 0 */ 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Setup transfer mode */ 47462306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) 47562306a36Sopenharmony_ci conf |= SPICC_POL; 47662306a36Sopenharmony_ci else 47762306a36Sopenharmony_ci conf &= ~SPICC_POL; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (!spicc->data->has_oen) { 48062306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) { 48162306a36Sopenharmony_ci if (spicc->pins_idle_high) 48262306a36Sopenharmony_ci pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_high); 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci if (spicc->pins_idle_low) 48562306a36Sopenharmony_ci pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_low); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (spi->mode & SPI_CPHA) 49062306a36Sopenharmony_ci conf |= SPICC_PHA; 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci conf &= ~SPICC_PHA; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* SSCTL = 0 */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (spi->mode & SPI_CS_HIGH) 49762306a36Sopenharmony_ci conf |= SPICC_SSPOL; 49862306a36Sopenharmony_ci else 49962306a36Sopenharmony_ci conf &= ~SPICC_SSPOL; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (spi->mode & SPI_READY) 50262306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_LOWLEVEL); 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_IGNORE); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* Select CS */ 50762306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_CS_MASK, spi_get_chipselect(spi, 0)); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Default 8bit word */ 51062306a36Sopenharmony_ci conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci writel_relaxed(conf, spicc->base + SPICC_CONREG); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Setup no wait cycles by default */ 51562306a36Sopenharmony_ci writel_relaxed(0, spicc->base + SPICC_PERIODREG); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int meson_spicc_unprepare_transfer(struct spi_master *master) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct meson_spicc_device *spicc = spi_master_get_devdata(master); 52562306a36Sopenharmony_ci u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Disable all IRQs */ 52862306a36Sopenharmony_ci writel(0, spicc->base + SPICC_INTREG); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci device_reset_optional(&spicc->pdev->dev); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Set default configuration, keeping datarate field */ 53362306a36Sopenharmony_ci writel_relaxed(conf, spicc->base + SPICC_CONREG); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (!spicc->data->has_oen) 53662306a36Sopenharmony_ci pinctrl_select_default_state(&spicc->pdev->dev); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int meson_spicc_setup(struct spi_device *spi) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci if (!spi->controller_state) 54462306a36Sopenharmony_ci spi->controller_state = spi_master_get_devdata(spi->master); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void meson_spicc_cleanup(struct spi_device *spi) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci spi->controller_state = NULL; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * The Clock Mux 55662306a36Sopenharmony_ci * x-----------------x x------------x x------\ 55762306a36Sopenharmony_ci * |---| pow2 fixed div |---| pow2 div |----| | 55862306a36Sopenharmony_ci * | x-----------------x x------------x | | 55962306a36Sopenharmony_ci * src ---| | mux |-- out 56062306a36Sopenharmony_ci * | x-----------------x x------------x | | 56162306a36Sopenharmony_ci * |---| enh fixed div |---| enh div |0---| | 56262306a36Sopenharmony_ci * x-----------------x x------------x x------/ 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Clk path for GX series: 56562306a36Sopenharmony_ci * src -> pow2 fixed div -> pow2 div -> out 56662306a36Sopenharmony_ci * 56762306a36Sopenharmony_ci * Clk path for AXG series: 56862306a36Sopenharmony_ci * src -> pow2 fixed div -> pow2 div -> mux -> out 56962306a36Sopenharmony_ci * src -> enh fixed div -> enh div -> mux -> out 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Clk path for G12A series: 57262306a36Sopenharmony_ci * pclk -> pow2 fixed div -> pow2 div -> mux -> out 57362306a36Sopenharmony_ci * pclk -> enh fixed div -> enh div -> mux -> out 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * The pow2 divider is tied to the controller HW state, and the 57662306a36Sopenharmony_ci * divider is only valid when the controller is initialized. 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * A set of clock ops is added to make sure we don't read/set this 57962306a36Sopenharmony_ci * clock rate while the controller is in an unknown state. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw, 58362306a36Sopenharmony_ci unsigned long parent_rate) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 58662306a36Sopenharmony_ci struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!spicc->master->cur_msg) 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return clk_divider_ops.recalc_rate(hw, parent_rate); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic int meson_spicc_pow2_determine_rate(struct clk_hw *hw, 59562306a36Sopenharmony_ci struct clk_rate_request *req) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 59862306a36Sopenharmony_ci struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!spicc->master->cur_msg) 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return clk_divider_ops.determine_rate(hw, req); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate, 60762306a36Sopenharmony_ci unsigned long parent_rate) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 61062306a36Sopenharmony_ci struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (!spicc->master->cur_msg) 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return clk_divider_ops.set_rate(hw, rate, parent_rate); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic const struct clk_ops meson_spicc_pow2_clk_ops = { 61962306a36Sopenharmony_ci .recalc_rate = meson_spicc_pow2_recalc_rate, 62062306a36Sopenharmony_ci .determine_rate = meson_spicc_pow2_determine_rate, 62162306a36Sopenharmony_ci .set_rate = meson_spicc_pow2_set_rate, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct device *dev = &spicc->pdev->dev; 62762306a36Sopenharmony_ci struct clk_fixed_factor *pow2_fixed_div; 62862306a36Sopenharmony_ci struct clk_init_data init; 62962306a36Sopenharmony_ci struct clk *clk; 63062306a36Sopenharmony_ci struct clk_parent_data parent_data[2]; 63162306a36Sopenharmony_ci char name[64]; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 63462306a36Sopenharmony_ci memset(&parent_data, 0, sizeof(parent_data)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci init.parent_data = parent_data; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* algorithm for pow2 div: rate = freq / 4 / (2 ^ N) */ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci pow2_fixed_div = devm_kzalloc(dev, sizeof(*pow2_fixed_div), GFP_KERNEL); 64162306a36Sopenharmony_ci if (!pow2_fixed_div) 64262306a36Sopenharmony_ci return -ENOMEM; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev)); 64562306a36Sopenharmony_ci init.name = name; 64662306a36Sopenharmony_ci init.ops = &clk_fixed_factor_ops; 64762306a36Sopenharmony_ci init.flags = 0; 64862306a36Sopenharmony_ci if (spicc->data->has_pclk) 64962306a36Sopenharmony_ci parent_data[0].hw = __clk_get_hw(spicc->pclk); 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci parent_data[0].hw = __clk_get_hw(spicc->core); 65262306a36Sopenharmony_ci init.num_parents = 1; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci pow2_fixed_div->mult = 1, 65562306a36Sopenharmony_ci pow2_fixed_div->div = 4, 65662306a36Sopenharmony_ci pow2_fixed_div->hw.init = &init; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci clk = devm_clk_register(dev, &pow2_fixed_div->hw); 65962306a36Sopenharmony_ci if (WARN_ON(IS_ERR(clk))) 66062306a36Sopenharmony_ci return PTR_ERR(clk); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev)); 66362306a36Sopenharmony_ci init.name = name; 66462306a36Sopenharmony_ci init.ops = &meson_spicc_pow2_clk_ops; 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * Set NOCACHE here to make sure we read the actual HW value 66762306a36Sopenharmony_ci * since we reset the HW after each transfer. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; 67062306a36Sopenharmony_ci parent_data[0].hw = &pow2_fixed_div->hw; 67162306a36Sopenharmony_ci init.num_parents = 1; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci spicc->pow2_div.shift = 16, 67462306a36Sopenharmony_ci spicc->pow2_div.width = 3, 67562306a36Sopenharmony_ci spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO, 67662306a36Sopenharmony_ci spicc->pow2_div.reg = spicc->base + SPICC_CONREG; 67762306a36Sopenharmony_ci spicc->pow2_div.hw.init = &init; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw); 68062306a36Sopenharmony_ci if (WARN_ON(IS_ERR(spicc->clk))) 68162306a36Sopenharmony_ci return PTR_ERR(spicc->clk); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct device *dev = &spicc->pdev->dev; 68962306a36Sopenharmony_ci struct clk_fixed_factor *enh_fixed_div; 69062306a36Sopenharmony_ci struct clk_divider *enh_div; 69162306a36Sopenharmony_ci struct clk_mux *mux; 69262306a36Sopenharmony_ci struct clk_init_data init; 69362306a36Sopenharmony_ci struct clk *clk; 69462306a36Sopenharmony_ci struct clk_parent_data parent_data[2]; 69562306a36Sopenharmony_ci char name[64]; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 69862306a36Sopenharmony_ci memset(&parent_data, 0, sizeof(parent_data)); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci init.parent_data = parent_data; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* algorithm for enh div: rate = freq / 2 / (N + 1) */ 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci enh_fixed_div = devm_kzalloc(dev, sizeof(*enh_fixed_div), GFP_KERNEL); 70562306a36Sopenharmony_ci if (!enh_fixed_div) 70662306a36Sopenharmony_ci return -ENOMEM; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev)); 70962306a36Sopenharmony_ci init.name = name; 71062306a36Sopenharmony_ci init.ops = &clk_fixed_factor_ops; 71162306a36Sopenharmony_ci init.flags = 0; 71262306a36Sopenharmony_ci if (spicc->data->has_pclk) 71362306a36Sopenharmony_ci parent_data[0].hw = __clk_get_hw(spicc->pclk); 71462306a36Sopenharmony_ci else 71562306a36Sopenharmony_ci parent_data[0].hw = __clk_get_hw(spicc->core); 71662306a36Sopenharmony_ci init.num_parents = 1; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci enh_fixed_div->mult = 1, 71962306a36Sopenharmony_ci enh_fixed_div->div = 2, 72062306a36Sopenharmony_ci enh_fixed_div->hw.init = &init; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci clk = devm_clk_register(dev, &enh_fixed_div->hw); 72362306a36Sopenharmony_ci if (WARN_ON(IS_ERR(clk))) 72462306a36Sopenharmony_ci return PTR_ERR(clk); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci enh_div = devm_kzalloc(dev, sizeof(*enh_div), GFP_KERNEL); 72762306a36Sopenharmony_ci if (!enh_div) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s#enh_div", dev_name(dev)); 73162306a36Sopenharmony_ci init.name = name; 73262306a36Sopenharmony_ci init.ops = &clk_divider_ops; 73362306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 73462306a36Sopenharmony_ci parent_data[0].hw = &enh_fixed_div->hw; 73562306a36Sopenharmony_ci init.num_parents = 1; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci enh_div->shift = 16, 73862306a36Sopenharmony_ci enh_div->width = 8, 73962306a36Sopenharmony_ci enh_div->reg = spicc->base + SPICC_ENH_CTL0; 74062306a36Sopenharmony_ci enh_div->hw.init = &init; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci clk = devm_clk_register(dev, &enh_div->hw); 74362306a36Sopenharmony_ci if (WARN_ON(IS_ERR(clk))) 74462306a36Sopenharmony_ci return PTR_ERR(clk); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 74762306a36Sopenharmony_ci if (!mux) 74862306a36Sopenharmony_ci return -ENOMEM; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s#sel", dev_name(dev)); 75162306a36Sopenharmony_ci init.name = name; 75262306a36Sopenharmony_ci init.ops = &clk_mux_ops; 75362306a36Sopenharmony_ci parent_data[0].hw = &spicc->pow2_div.hw; 75462306a36Sopenharmony_ci parent_data[1].hw = &enh_div->hw; 75562306a36Sopenharmony_ci init.num_parents = 2; 75662306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci mux->mask = 0x1, 75962306a36Sopenharmony_ci mux->shift = 24, 76062306a36Sopenharmony_ci mux->reg = spicc->base + SPICC_ENH_CTL0; 76162306a36Sopenharmony_ci mux->hw.init = &init; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci spicc->clk = devm_clk_register(dev, &mux->hw); 76462306a36Sopenharmony_ci if (WARN_ON(IS_ERR(spicc->clk))) 76562306a36Sopenharmony_ci return PTR_ERR(spicc->clk); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int meson_spicc_probe(struct platform_device *pdev) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct spi_master *master; 77362306a36Sopenharmony_ci struct meson_spicc_device *spicc; 77462306a36Sopenharmony_ci int ret, irq; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); 77762306a36Sopenharmony_ci if (!master) { 77862306a36Sopenharmony_ci dev_err(&pdev->dev, "master allocation failed\n"); 77962306a36Sopenharmony_ci return -ENOMEM; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci spicc = spi_master_get_devdata(master); 78262306a36Sopenharmony_ci spicc->master = master; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci spicc->data = of_device_get_match_data(&pdev->dev); 78562306a36Sopenharmony_ci if (!spicc->data) { 78662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get match data\n"); 78762306a36Sopenharmony_ci ret = -EINVAL; 78862306a36Sopenharmony_ci goto out_master; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci spicc->pdev = pdev; 79262306a36Sopenharmony_ci platform_set_drvdata(pdev, spicc); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci init_completion(&spicc->done); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci spicc->base = devm_platform_ioremap_resource(pdev, 0); 79762306a36Sopenharmony_ci if (IS_ERR(spicc->base)) { 79862306a36Sopenharmony_ci dev_err(&pdev->dev, "io resource mapping failed\n"); 79962306a36Sopenharmony_ci ret = PTR_ERR(spicc->base); 80062306a36Sopenharmony_ci goto out_master; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* Set master mode and enable controller */ 80462306a36Sopenharmony_ci writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER, 80562306a36Sopenharmony_ci spicc->base + SPICC_CONREG); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Disable all IRQs */ 80862306a36Sopenharmony_ci writel_relaxed(0, spicc->base + SPICC_INTREG); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 81162306a36Sopenharmony_ci if (irq < 0) { 81262306a36Sopenharmony_ci ret = irq; 81362306a36Sopenharmony_ci goto out_master; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq, 81762306a36Sopenharmony_ci 0, NULL, spicc); 81862306a36Sopenharmony_ci if (ret) { 81962306a36Sopenharmony_ci dev_err(&pdev->dev, "irq request failed\n"); 82062306a36Sopenharmony_ci goto out_master; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci spicc->core = devm_clk_get(&pdev->dev, "core"); 82462306a36Sopenharmony_ci if (IS_ERR(spicc->core)) { 82562306a36Sopenharmony_ci dev_err(&pdev->dev, "core clock request failed\n"); 82662306a36Sopenharmony_ci ret = PTR_ERR(spicc->core); 82762306a36Sopenharmony_ci goto out_master; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (spicc->data->has_pclk) { 83162306a36Sopenharmony_ci spicc->pclk = devm_clk_get(&pdev->dev, "pclk"); 83262306a36Sopenharmony_ci if (IS_ERR(spicc->pclk)) { 83362306a36Sopenharmony_ci dev_err(&pdev->dev, "pclk clock request failed\n"); 83462306a36Sopenharmony_ci ret = PTR_ERR(spicc->pclk); 83562306a36Sopenharmony_ci goto out_master; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ret = clk_prepare_enable(spicc->core); 84062306a36Sopenharmony_ci if (ret) { 84162306a36Sopenharmony_ci dev_err(&pdev->dev, "core clock enable failed\n"); 84262306a36Sopenharmony_ci goto out_master; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ret = clk_prepare_enable(spicc->pclk); 84662306a36Sopenharmony_ci if (ret) { 84762306a36Sopenharmony_ci dev_err(&pdev->dev, "pclk clock enable failed\n"); 84862306a36Sopenharmony_ci goto out_core_clk; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci spicc->pinctrl = devm_pinctrl_get(&pdev->dev); 85262306a36Sopenharmony_ci if (IS_ERR(spicc->pinctrl)) { 85362306a36Sopenharmony_ci ret = PTR_ERR(spicc->pinctrl); 85462306a36Sopenharmony_ci goto out_clk; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci device_reset_optional(&pdev->dev); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci master->num_chipselect = 4; 86062306a36Sopenharmony_ci master->dev.of_node = pdev->dev.of_node; 86162306a36Sopenharmony_ci master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH; 86262306a36Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(32) | 86362306a36Sopenharmony_ci SPI_BPW_MASK(24) | 86462306a36Sopenharmony_ci SPI_BPW_MASK(16) | 86562306a36Sopenharmony_ci SPI_BPW_MASK(8); 86662306a36Sopenharmony_ci master->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX); 86762306a36Sopenharmony_ci master->min_speed_hz = spicc->data->min_speed_hz; 86862306a36Sopenharmony_ci master->max_speed_hz = spicc->data->max_speed_hz; 86962306a36Sopenharmony_ci master->setup = meson_spicc_setup; 87062306a36Sopenharmony_ci master->cleanup = meson_spicc_cleanup; 87162306a36Sopenharmony_ci master->prepare_message = meson_spicc_prepare_message; 87262306a36Sopenharmony_ci master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer; 87362306a36Sopenharmony_ci master->transfer_one = meson_spicc_transfer_one; 87462306a36Sopenharmony_ci master->use_gpio_descriptors = true; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci meson_spicc_oen_enable(spicc); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci ret = meson_spicc_pow2_clk_init(spicc); 87962306a36Sopenharmony_ci if (ret) { 88062306a36Sopenharmony_ci dev_err(&pdev->dev, "pow2 clock registration failed\n"); 88162306a36Sopenharmony_ci goto out_clk; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (spicc->data->has_enhance_clk_div) { 88562306a36Sopenharmony_ci ret = meson_spicc_enh_clk_init(spicc); 88662306a36Sopenharmony_ci if (ret) { 88762306a36Sopenharmony_ci dev_err(&pdev->dev, "clock registration failed\n"); 88862306a36Sopenharmony_ci goto out_clk; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ret = devm_spi_register_master(&pdev->dev, master); 89362306a36Sopenharmony_ci if (ret) { 89462306a36Sopenharmony_ci dev_err(&pdev->dev, "spi master registration failed\n"); 89562306a36Sopenharmony_ci goto out_clk; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciout_clk: 90162306a36Sopenharmony_ci clk_disable_unprepare(spicc->pclk); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ciout_core_clk: 90462306a36Sopenharmony_ci clk_disable_unprepare(spicc->core); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciout_master: 90762306a36Sopenharmony_ci spi_master_put(master); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void meson_spicc_remove(struct platform_device *pdev) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct meson_spicc_device *spicc = platform_get_drvdata(pdev); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Disable SPI */ 91762306a36Sopenharmony_ci writel(0, spicc->base + SPICC_CONREG); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci clk_disable_unprepare(spicc->core); 92062306a36Sopenharmony_ci clk_disable_unprepare(spicc->pclk); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci spi_master_put(spicc->master); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_gx_data = { 92662306a36Sopenharmony_ci .max_speed_hz = 30000000, 92762306a36Sopenharmony_ci .min_speed_hz = 325000, 92862306a36Sopenharmony_ci .fifo_size = 16, 92962306a36Sopenharmony_ci}; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_axg_data = { 93262306a36Sopenharmony_ci .max_speed_hz = 80000000, 93362306a36Sopenharmony_ci .min_speed_hz = 325000, 93462306a36Sopenharmony_ci .fifo_size = 16, 93562306a36Sopenharmony_ci .has_oen = true, 93662306a36Sopenharmony_ci .has_enhance_clk_div = true, 93762306a36Sopenharmony_ci}; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_g12a_data = { 94062306a36Sopenharmony_ci .max_speed_hz = 166666666, 94162306a36Sopenharmony_ci .min_speed_hz = 50000, 94262306a36Sopenharmony_ci .fifo_size = 15, 94362306a36Sopenharmony_ci .has_oen = true, 94462306a36Sopenharmony_ci .has_enhance_clk_div = true, 94562306a36Sopenharmony_ci .has_pclk = true, 94662306a36Sopenharmony_ci}; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic const struct of_device_id meson_spicc_of_match[] = { 94962306a36Sopenharmony_ci { 95062306a36Sopenharmony_ci .compatible = "amlogic,meson-gx-spicc", 95162306a36Sopenharmony_ci .data = &meson_spicc_gx_data, 95262306a36Sopenharmony_ci }, 95362306a36Sopenharmony_ci { 95462306a36Sopenharmony_ci .compatible = "amlogic,meson-axg-spicc", 95562306a36Sopenharmony_ci .data = &meson_spicc_axg_data, 95662306a36Sopenharmony_ci }, 95762306a36Sopenharmony_ci { 95862306a36Sopenharmony_ci .compatible = "amlogic,meson-g12a-spicc", 95962306a36Sopenharmony_ci .data = &meson_spicc_g12a_data, 96062306a36Sopenharmony_ci }, 96162306a36Sopenharmony_ci { /* sentinel */ } 96262306a36Sopenharmony_ci}; 96362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_spicc_of_match); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic struct platform_driver meson_spicc_driver = { 96662306a36Sopenharmony_ci .probe = meson_spicc_probe, 96762306a36Sopenharmony_ci .remove_new = meson_spicc_remove, 96862306a36Sopenharmony_ci .driver = { 96962306a36Sopenharmony_ci .name = "meson-spicc", 97062306a36Sopenharmony_ci .of_match_table = of_match_ptr(meson_spicc_of_match), 97162306a36Sopenharmony_ci }, 97262306a36Sopenharmony_ci}; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cimodule_platform_driver(meson_spicc_driver); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciMODULE_DESCRIPTION("Meson SPI Communication Controller driver"); 97762306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 97862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 979