162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Diolan DLN-2 USB-SPI adapter 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Intel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/property.h> 1262306a36Sopenharmony_ci#include <linux/mfd/dln2.h> 1362306a36Sopenharmony_ci#include <linux/spi/spi.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <asm/unaligned.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DLN2_SPI_MODULE_ID 0x02 1862306a36Sopenharmony_ci#define DLN2_SPI_CMD(cmd) DLN2_CMD(cmd, DLN2_SPI_MODULE_ID) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* SPI commands */ 2162306a36Sopenharmony_ci#define DLN2_SPI_GET_PORT_COUNT DLN2_SPI_CMD(0x00) 2262306a36Sopenharmony_ci#define DLN2_SPI_ENABLE DLN2_SPI_CMD(0x11) 2362306a36Sopenharmony_ci#define DLN2_SPI_DISABLE DLN2_SPI_CMD(0x12) 2462306a36Sopenharmony_ci#define DLN2_SPI_IS_ENABLED DLN2_SPI_CMD(0x13) 2562306a36Sopenharmony_ci#define DLN2_SPI_SET_MODE DLN2_SPI_CMD(0x14) 2662306a36Sopenharmony_ci#define DLN2_SPI_GET_MODE DLN2_SPI_CMD(0x15) 2762306a36Sopenharmony_ci#define DLN2_SPI_SET_FRAME_SIZE DLN2_SPI_CMD(0x16) 2862306a36Sopenharmony_ci#define DLN2_SPI_GET_FRAME_SIZE DLN2_SPI_CMD(0x17) 2962306a36Sopenharmony_ci#define DLN2_SPI_SET_FREQUENCY DLN2_SPI_CMD(0x18) 3062306a36Sopenharmony_ci#define DLN2_SPI_GET_FREQUENCY DLN2_SPI_CMD(0x19) 3162306a36Sopenharmony_ci#define DLN2_SPI_READ_WRITE DLN2_SPI_CMD(0x1A) 3262306a36Sopenharmony_ci#define DLN2_SPI_READ DLN2_SPI_CMD(0x1B) 3362306a36Sopenharmony_ci#define DLN2_SPI_WRITE DLN2_SPI_CMD(0x1C) 3462306a36Sopenharmony_ci#define DLN2_SPI_SET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x20) 3562306a36Sopenharmony_ci#define DLN2_SPI_GET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x21) 3662306a36Sopenharmony_ci#define DLN2_SPI_SET_DELAY_AFTER_SS DLN2_SPI_CMD(0x22) 3762306a36Sopenharmony_ci#define DLN2_SPI_GET_DELAY_AFTER_SS DLN2_SPI_CMD(0x23) 3862306a36Sopenharmony_ci#define DLN2_SPI_SET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x24) 3962306a36Sopenharmony_ci#define DLN2_SPI_GET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x25) 4062306a36Sopenharmony_ci#define DLN2_SPI_SET_SS DLN2_SPI_CMD(0x26) 4162306a36Sopenharmony_ci#define DLN2_SPI_GET_SS DLN2_SPI_CMD(0x27) 4262306a36Sopenharmony_ci#define DLN2_SPI_RELEASE_SS DLN2_SPI_CMD(0x28) 4362306a36Sopenharmony_ci#define DLN2_SPI_SS_VARIABLE_ENABLE DLN2_SPI_CMD(0x2B) 4462306a36Sopenharmony_ci#define DLN2_SPI_SS_VARIABLE_DISABLE DLN2_SPI_CMD(0x2C) 4562306a36Sopenharmony_ci#define DLN2_SPI_SS_VARIABLE_IS_ENABLED DLN2_SPI_CMD(0x2D) 4662306a36Sopenharmony_ci#define DLN2_SPI_SS_AAT_ENABLE DLN2_SPI_CMD(0x2E) 4762306a36Sopenharmony_ci#define DLN2_SPI_SS_AAT_DISABLE DLN2_SPI_CMD(0x2F) 4862306a36Sopenharmony_ci#define DLN2_SPI_SS_AAT_IS_ENABLED DLN2_SPI_CMD(0x30) 4962306a36Sopenharmony_ci#define DLN2_SPI_SS_BETWEEN_FRAMES_ENABLE DLN2_SPI_CMD(0x31) 5062306a36Sopenharmony_ci#define DLN2_SPI_SS_BETWEEN_FRAMES_DISABLE DLN2_SPI_CMD(0x32) 5162306a36Sopenharmony_ci#define DLN2_SPI_SS_BETWEEN_FRAMES_IS_ENABLED DLN2_SPI_CMD(0x33) 5262306a36Sopenharmony_ci#define DLN2_SPI_SET_CPHA DLN2_SPI_CMD(0x34) 5362306a36Sopenharmony_ci#define DLN2_SPI_GET_CPHA DLN2_SPI_CMD(0x35) 5462306a36Sopenharmony_ci#define DLN2_SPI_SET_CPOL DLN2_SPI_CMD(0x36) 5562306a36Sopenharmony_ci#define DLN2_SPI_GET_CPOL DLN2_SPI_CMD(0x37) 5662306a36Sopenharmony_ci#define DLN2_SPI_SS_MULTI_ENABLE DLN2_SPI_CMD(0x38) 5762306a36Sopenharmony_ci#define DLN2_SPI_SS_MULTI_DISABLE DLN2_SPI_CMD(0x39) 5862306a36Sopenharmony_ci#define DLN2_SPI_SS_MULTI_IS_ENABLED DLN2_SPI_CMD(0x3A) 5962306a36Sopenharmony_ci#define DLN2_SPI_GET_SUPPORTED_MODES DLN2_SPI_CMD(0x40) 6062306a36Sopenharmony_ci#define DLN2_SPI_GET_SUPPORTED_CPHA_VALUES DLN2_SPI_CMD(0x41) 6162306a36Sopenharmony_ci#define DLN2_SPI_GET_SUPPORTED_CPOL_VALUES DLN2_SPI_CMD(0x42) 6262306a36Sopenharmony_ci#define DLN2_SPI_GET_SUPPORTED_FRAME_SIZES DLN2_SPI_CMD(0x43) 6362306a36Sopenharmony_ci#define DLN2_SPI_GET_SS_COUNT DLN2_SPI_CMD(0x44) 6462306a36Sopenharmony_ci#define DLN2_SPI_GET_MIN_FREQUENCY DLN2_SPI_CMD(0x45) 6562306a36Sopenharmony_ci#define DLN2_SPI_GET_MAX_FREQUENCY DLN2_SPI_CMD(0x46) 6662306a36Sopenharmony_ci#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x47) 6762306a36Sopenharmony_ci#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x48) 6862306a36Sopenharmony_ci#define DLN2_SPI_GET_MIN_DELAY_AFTER_SS DLN2_SPI_CMD(0x49) 6962306a36Sopenharmony_ci#define DLN2_SPI_GET_MAX_DELAY_AFTER_SS DLN2_SPI_CMD(0x4A) 7062306a36Sopenharmony_ci#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4B) 7162306a36Sopenharmony_ci#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4C) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DLN2_SPI_MAX_XFER_SIZE 256 7462306a36Sopenharmony_ci#define DLN2_SPI_BUF_SIZE (DLN2_SPI_MAX_XFER_SIZE + 16) 7562306a36Sopenharmony_ci#define DLN2_SPI_ATTR_LEAVE_SS_LOW BIT(0) 7662306a36Sopenharmony_ci#define DLN2_TRANSFERS_WAIT_COMPLETE 1 7762306a36Sopenharmony_ci#define DLN2_TRANSFERS_CANCEL 0 7862306a36Sopenharmony_ci#define DLN2_RPM_AUTOSUSPEND_TIMEOUT 2000 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct dln2_spi { 8162306a36Sopenharmony_ci struct platform_device *pdev; 8262306a36Sopenharmony_ci struct spi_controller *host; 8362306a36Sopenharmony_ci u8 port; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * This buffer will be used mainly for read/write operations. Since 8762306a36Sopenharmony_ci * they're quite large, we cannot use the stack. Protection is not 8862306a36Sopenharmony_ci * needed because all SPI communication is serialized by the SPI core. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci void *buf; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci u8 bpw; 9362306a36Sopenharmony_ci u32 speed; 9462306a36Sopenharmony_ci u16 mode; 9562306a36Sopenharmony_ci u8 cs; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Enable/Disable SPI module. The disable command will wait for transfers to 10062306a36Sopenharmony_ci * complete first. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic int dln2_spi_enable(struct dln2_spi *dln2, bool enable) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u16 cmd; 10562306a36Sopenharmony_ci struct { 10662306a36Sopenharmony_ci u8 port; 10762306a36Sopenharmony_ci u8 wait_for_completion; 10862306a36Sopenharmony_ci } tx; 10962306a36Sopenharmony_ci unsigned len = sizeof(tx); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci tx.port = dln2->port; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (enable) { 11462306a36Sopenharmony_ci cmd = DLN2_SPI_ENABLE; 11562306a36Sopenharmony_ci len -= sizeof(tx.wait_for_completion); 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE; 11862306a36Sopenharmony_ci cmd = DLN2_SPI_DISABLE; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, cmd, &tx, len); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Select/unselect multiple CS lines. The selected lines will be automatically 12662306a36Sopenharmony_ci * toggled LOW/HIGH by the board firmware during transfers, provided they're 12762306a36Sopenharmony_ci * enabled first. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation 13062306a36Sopenharmony_ci * will toggle the lines LOW/HIGH automatically. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic int dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct { 13562306a36Sopenharmony_ci u8 port; 13662306a36Sopenharmony_ci u8 cs; 13762306a36Sopenharmony_ci } tx; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci tx.port = dln2->port; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * According to Diolan docs, "a slave device can be selected by changing 14362306a36Sopenharmony_ci * the corresponding bit value to 0". The rest must be set to 1. Hence 14462306a36Sopenharmony_ci * the bitwise NOT in front. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci tx.cs = ~cs_mask; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_SS, &tx, sizeof(tx)); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Select one CS line. The other lines will be un-selected. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return dln2_spi_cs_set(dln2, BIT(cs)); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Enable/disable CS lines for usage. The module has to be disabled first. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic int dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct { 16562306a36Sopenharmony_ci u8 port; 16662306a36Sopenharmony_ci u8 cs; 16762306a36Sopenharmony_ci } tx; 16862306a36Sopenharmony_ci u16 cmd; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci tx.port = dln2->port; 17162306a36Sopenharmony_ci tx.cs = cs_mask; 17262306a36Sopenharmony_ci cmd = enable ? DLN2_SPI_SS_MULTI_ENABLE : DLN2_SPI_SS_MULTI_DISABLE; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int dln2_spi_cs_enable_all(struct dln2_spi *dln2, bool enable) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u8 cs_mask = GENMASK(dln2->host->num_chipselect - 1, 0); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return dln2_spi_cs_enable(dln2, cs_mask, enable); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int ret; 18762306a36Sopenharmony_ci struct { 18862306a36Sopenharmony_ci u8 port; 18962306a36Sopenharmony_ci } tx; 19062306a36Sopenharmony_ci struct { 19162306a36Sopenharmony_ci __le16 cs_count; 19262306a36Sopenharmony_ci } rx; 19362306a36Sopenharmony_ci unsigned rx_len = sizeof(rx); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci tx.port = dln2->port; 19662306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SS_COUNT, &tx, sizeof(tx), 19762306a36Sopenharmony_ci &rx, &rx_len); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci if (rx_len < sizeof(rx)) 20162306a36Sopenharmony_ci return -EPROTO; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci *cs_num = le16_to_cpu(rx.cs_count); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int dln2_spi_get_speed(struct dln2_spi *dln2, u16 cmd, u32 *freq) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci struct { 21462306a36Sopenharmony_ci u8 port; 21562306a36Sopenharmony_ci } tx; 21662306a36Sopenharmony_ci struct { 21762306a36Sopenharmony_ci __le32 speed; 21862306a36Sopenharmony_ci } rx; 21962306a36Sopenharmony_ci unsigned rx_len = sizeof(rx); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci tx.port = dln2->port; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, cmd, &tx, sizeof(tx), &rx, &rx_len); 22462306a36Sopenharmony_ci if (ret < 0) 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci if (rx_len < sizeof(rx)) 22762306a36Sopenharmony_ci return -EPROTO; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci *freq = le32_to_cpu(rx.speed); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Get bus min/max frequencies. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int dln2_spi_get_speed_range(struct dln2_spi *dln2, u32 *fmin, u32 *fmax) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MIN_FREQUENCY, fmin); 24262306a36Sopenharmony_ci if (ret < 0) 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MAX_FREQUENCY, fmax); 24662306a36Sopenharmony_ci if (ret < 0) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dev_dbg(&dln2->pdev->dev, "freq_min = %d, freq_max = %d\n", 25062306a36Sopenharmony_ci *fmin, *fmax); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * Set the bus speed. The module will automatically round down to the closest 25762306a36Sopenharmony_ci * available frequency and returns it. The module has to be disabled first. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic int dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci struct { 26362306a36Sopenharmony_ci u8 port; 26462306a36Sopenharmony_ci __le32 speed; 26562306a36Sopenharmony_ci } __packed tx; 26662306a36Sopenharmony_ci struct { 26762306a36Sopenharmony_ci __le32 speed; 26862306a36Sopenharmony_ci } rx; 26962306a36Sopenharmony_ci int rx_len = sizeof(rx); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci tx.port = dln2->port; 27262306a36Sopenharmony_ci tx.speed = cpu_to_le32(speed); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_SPI_SET_FREQUENCY, &tx, sizeof(tx), 27562306a36Sopenharmony_ci &rx, &rx_len); 27662306a36Sopenharmony_ci if (ret < 0) 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci if (rx_len < sizeof(rx)) 27962306a36Sopenharmony_ci return -EPROTO; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* 28562306a36Sopenharmony_ci * Change CPOL & CPHA. The module has to be disabled first. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic int dln2_spi_set_mode(struct dln2_spi *dln2, u8 mode) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct { 29062306a36Sopenharmony_ci u8 port; 29162306a36Sopenharmony_ci u8 mode; 29262306a36Sopenharmony_ci } tx; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci tx.port = dln2->port; 29562306a36Sopenharmony_ci tx.mode = mode; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_MODE, &tx, sizeof(tx)); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* 30162306a36Sopenharmony_ci * Change frame size. The module has to be disabled first. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_cistatic int dln2_spi_set_bpw(struct dln2_spi *dln2, u8 bpw) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct { 30662306a36Sopenharmony_ci u8 port; 30762306a36Sopenharmony_ci u8 bpw; 30862306a36Sopenharmony_ci } tx; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci tx.port = dln2->port; 31162306a36Sopenharmony_ci tx.bpw = bpw; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_FRAME_SIZE, 31462306a36Sopenharmony_ci &tx, sizeof(tx)); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int dln2_spi_get_supported_frame_sizes(struct dln2_spi *dln2, 31862306a36Sopenharmony_ci u32 *bpw_mask) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int ret; 32162306a36Sopenharmony_ci struct { 32262306a36Sopenharmony_ci u8 port; 32362306a36Sopenharmony_ci } tx; 32462306a36Sopenharmony_ci struct { 32562306a36Sopenharmony_ci u8 count; 32662306a36Sopenharmony_ci u8 frame_sizes[36]; 32762306a36Sopenharmony_ci } *rx = dln2->buf; 32862306a36Sopenharmony_ci unsigned rx_len = sizeof(*rx); 32962306a36Sopenharmony_ci int i; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci tx.port = dln2->port; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SUPPORTED_FRAME_SIZES, 33462306a36Sopenharmony_ci &tx, sizeof(tx), rx, &rx_len); 33562306a36Sopenharmony_ci if (ret < 0) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci if (rx_len < sizeof(*rx)) 33862306a36Sopenharmony_ci return -EPROTO; 33962306a36Sopenharmony_ci if (rx->count > ARRAY_SIZE(rx->frame_sizes)) 34062306a36Sopenharmony_ci return -EPROTO; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci *bpw_mask = 0; 34362306a36Sopenharmony_ci for (i = 0; i < rx->count; i++) 34462306a36Sopenharmony_ci *bpw_mask |= BIT(rx->frame_sizes[i] - 1); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci dev_dbg(&dln2->pdev->dev, "bpw_mask = 0x%X\n", *bpw_mask); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* 35262306a36Sopenharmony_ci * Copy the data to DLN2 buffer and change the byte order to LE, requested by 35362306a36Sopenharmony_ci * DLN2 module. SPI core makes sure that the data length is a multiple of word 35462306a36Sopenharmony_ci * size. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_cistatic int dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 35962306a36Sopenharmony_ci memcpy(dln2_buf, src, len); 36062306a36Sopenharmony_ci#else 36162306a36Sopenharmony_ci if (bpw <= 8) { 36262306a36Sopenharmony_ci memcpy(dln2_buf, src, len); 36362306a36Sopenharmony_ci } else if (bpw <= 16) { 36462306a36Sopenharmony_ci __le16 *d = (__le16 *)dln2_buf; 36562306a36Sopenharmony_ci u16 *s = (u16 *)src; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci len = len / 2; 36862306a36Sopenharmony_ci while (len--) 36962306a36Sopenharmony_ci *d++ = cpu_to_le16p(s++); 37062306a36Sopenharmony_ci } else { 37162306a36Sopenharmony_ci __le32 *d = (__le32 *)dln2_buf; 37262306a36Sopenharmony_ci u32 *s = (u32 *)src; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci len = len / 4; 37562306a36Sopenharmony_ci while (len--) 37662306a36Sopenharmony_ci *d++ = cpu_to_le32p(s++); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci#endif 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2 38562306a36Sopenharmony_ci * buffer is LE ordered. SPI core makes sure that the data length is a multiple 38662306a36Sopenharmony_ci * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make 38762306a36Sopenharmony_ci * sure we avoid unaligned accesses for 32 bit case. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic int dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 39262306a36Sopenharmony_ci memcpy(dest, dln2_buf, len); 39362306a36Sopenharmony_ci#else 39462306a36Sopenharmony_ci if (bpw <= 8) { 39562306a36Sopenharmony_ci memcpy(dest, dln2_buf, len); 39662306a36Sopenharmony_ci } else if (bpw <= 16) { 39762306a36Sopenharmony_ci u16 *d = (u16 *)dest; 39862306a36Sopenharmony_ci __le16 *s = (__le16 *)dln2_buf; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci len = len / 2; 40162306a36Sopenharmony_ci while (len--) 40262306a36Sopenharmony_ci *d++ = le16_to_cpup(s++); 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci u32 *d = (u32 *)dest; 40562306a36Sopenharmony_ci __le32 *s = (__le32 *)dln2_buf; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci len = len / 4; 40862306a36Sopenharmony_ci while (len--) 40962306a36Sopenharmony_ci *d++ = get_unaligned_le32(s++); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * Perform one write operation. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_cistatic int dln2_spi_write_one(struct dln2_spi *dln2, const u8 *data, 42062306a36Sopenharmony_ci u16 data_len, u8 attr) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct { 42362306a36Sopenharmony_ci u8 port; 42462306a36Sopenharmony_ci __le16 size; 42562306a36Sopenharmony_ci u8 attr; 42662306a36Sopenharmony_ci u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 42762306a36Sopenharmony_ci } __packed *tx = dln2->buf; 42862306a36Sopenharmony_ci unsigned tx_len; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (data_len > DLN2_SPI_MAX_XFER_SIZE) 43362306a36Sopenharmony_ci return -EINVAL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci tx->port = dln2->port; 43662306a36Sopenharmony_ci tx->size = cpu_to_le16(data_len); 43762306a36Sopenharmony_ci tx->attr = attr; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci dln2_spi_copy_to_buf(tx->buf, data, data_len, dln2->bpw); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; 44262306a36Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, DLN2_SPI_WRITE, tx, tx_len); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * Perform one read operation. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_cistatic int dln2_spi_read_one(struct dln2_spi *dln2, u8 *data, 44962306a36Sopenharmony_ci u16 data_len, u8 attr) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci struct { 45362306a36Sopenharmony_ci u8 port; 45462306a36Sopenharmony_ci __le16 size; 45562306a36Sopenharmony_ci u8 attr; 45662306a36Sopenharmony_ci } __packed tx; 45762306a36Sopenharmony_ci struct { 45862306a36Sopenharmony_ci __le16 size; 45962306a36Sopenharmony_ci u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 46062306a36Sopenharmony_ci } __packed *rx = dln2->buf; 46162306a36Sopenharmony_ci unsigned rx_len = sizeof(*rx); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(*rx) > DLN2_SPI_BUF_SIZE); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (data_len > DLN2_SPI_MAX_XFER_SIZE) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci tx.port = dln2->port; 46962306a36Sopenharmony_ci tx.size = cpu_to_le16(data_len); 47062306a36Sopenharmony_ci tx.attr = attr; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ, &tx, sizeof(tx), 47362306a36Sopenharmony_ci rx, &rx_len); 47462306a36Sopenharmony_ci if (ret < 0) 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci if (rx_len < sizeof(rx->size) + data_len) 47762306a36Sopenharmony_ci return -EPROTO; 47862306a36Sopenharmony_ci if (le16_to_cpu(rx->size) != data_len) 47962306a36Sopenharmony_ci return -EPROTO; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci dln2_spi_copy_from_buf(data, rx->buf, data_len, dln2->bpw); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* 48762306a36Sopenharmony_ci * Perform one write & read operation. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistatic int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data, 49062306a36Sopenharmony_ci u8 *rx_data, u16 data_len, u8 attr) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int ret; 49362306a36Sopenharmony_ci struct { 49462306a36Sopenharmony_ci u8 port; 49562306a36Sopenharmony_ci __le16 size; 49662306a36Sopenharmony_ci u8 attr; 49762306a36Sopenharmony_ci u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 49862306a36Sopenharmony_ci } __packed *tx; 49962306a36Sopenharmony_ci struct { 50062306a36Sopenharmony_ci __le16 size; 50162306a36Sopenharmony_ci u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 50262306a36Sopenharmony_ci } __packed *rx; 50362306a36Sopenharmony_ci unsigned tx_len, rx_len; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE || 50662306a36Sopenharmony_ci sizeof(*rx) > DLN2_SPI_BUF_SIZE); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (data_len > DLN2_SPI_MAX_XFER_SIZE) 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * Since this is a pseudo full-duplex communication, we're perfectly 51362306a36Sopenharmony_ci * safe to use the same buffer for both tx and rx. When DLN2 sends the 51462306a36Sopenharmony_ci * response back, with the rx data, we don't need the tx buffer anymore. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci tx = dln2->buf; 51762306a36Sopenharmony_ci rx = dln2->buf; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci tx->port = dln2->port; 52062306a36Sopenharmony_ci tx->size = cpu_to_le16(data_len); 52162306a36Sopenharmony_ci tx->attr = attr; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci dln2_spi_copy_to_buf(tx->buf, tx_data, data_len, dln2->bpw); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; 52662306a36Sopenharmony_ci rx_len = sizeof(*rx); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ_WRITE, tx, tx_len, 52962306a36Sopenharmony_ci rx, &rx_len); 53062306a36Sopenharmony_ci if (ret < 0) 53162306a36Sopenharmony_ci return ret; 53262306a36Sopenharmony_ci if (rx_len < sizeof(rx->size) + data_len) 53362306a36Sopenharmony_ci return -EPROTO; 53462306a36Sopenharmony_ci if (le16_to_cpu(rx->size) != data_len) 53562306a36Sopenharmony_ci return -EPROTO; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci dln2_spi_copy_from_buf(rx_data, rx->buf, data_len, dln2->bpw); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci/* 54362306a36Sopenharmony_ci * Read/Write wrapper. It will automatically split an operation into multiple 54462306a36Sopenharmony_ci * single ones due to device buffer constraints. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_cistatic int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data, 54762306a36Sopenharmony_ci u8 *rx_data, u16 data_len, u8 attr) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci u16 len; 55162306a36Sopenharmony_ci u8 temp_attr; 55262306a36Sopenharmony_ci u16 remaining = data_len; 55362306a36Sopenharmony_ci u16 offset; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci do { 55662306a36Sopenharmony_ci if (remaining > DLN2_SPI_MAX_XFER_SIZE) { 55762306a36Sopenharmony_ci len = DLN2_SPI_MAX_XFER_SIZE; 55862306a36Sopenharmony_ci temp_attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; 55962306a36Sopenharmony_ci } else { 56062306a36Sopenharmony_ci len = remaining; 56162306a36Sopenharmony_ci temp_attr = attr; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci offset = data_len - remaining; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (tx_data && rx_data) { 56762306a36Sopenharmony_ci ret = dln2_spi_read_write_one(dln2, 56862306a36Sopenharmony_ci tx_data + offset, 56962306a36Sopenharmony_ci rx_data + offset, 57062306a36Sopenharmony_ci len, temp_attr); 57162306a36Sopenharmony_ci } else if (tx_data) { 57262306a36Sopenharmony_ci ret = dln2_spi_write_one(dln2, 57362306a36Sopenharmony_ci tx_data + offset, 57462306a36Sopenharmony_ci len, temp_attr); 57562306a36Sopenharmony_ci } else if (rx_data) { 57662306a36Sopenharmony_ci ret = dln2_spi_read_one(dln2, 57762306a36Sopenharmony_ci rx_data + offset, 57862306a36Sopenharmony_ci len, temp_attr); 57962306a36Sopenharmony_ci } else { 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (ret < 0) 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci remaining -= len; 58762306a36Sopenharmony_ci } while (remaining); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int dln2_spi_prepare_message(struct spi_controller *host, 59362306a36Sopenharmony_ci struct spi_message *message) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci int ret; 59662306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 59762306a36Sopenharmony_ci struct spi_device *spi = message->spi; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (dln2->cs != spi_get_chipselect(spi, 0)) { 60062306a36Sopenharmony_ci ret = dln2_spi_cs_set_one(dln2, spi_get_chipselect(spi, 0)); 60162306a36Sopenharmony_ci if (ret < 0) 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dln2->cs = spi_get_chipselect(spi, 0); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed, 61162306a36Sopenharmony_ci u8 bpw, u8 mode) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int ret; 61462306a36Sopenharmony_ci bool bus_setup_change; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci bus_setup_change = dln2->speed != speed || dln2->mode != mode || 61762306a36Sopenharmony_ci dln2->bpw != bpw; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!bus_setup_change) 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = dln2_spi_enable(dln2, false); 62362306a36Sopenharmony_ci if (ret < 0) 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (dln2->speed != speed) { 62762306a36Sopenharmony_ci ret = dln2_spi_set_speed(dln2, speed); 62862306a36Sopenharmony_ci if (ret < 0) 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci dln2->speed = speed; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (dln2->mode != mode) { 63562306a36Sopenharmony_ci ret = dln2_spi_set_mode(dln2, mode & 0x3); 63662306a36Sopenharmony_ci if (ret < 0) 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci dln2->mode = mode; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (dln2->bpw != bpw) { 64362306a36Sopenharmony_ci ret = dln2_spi_set_bpw(dln2, bpw); 64462306a36Sopenharmony_ci if (ret < 0) 64562306a36Sopenharmony_ci return ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci dln2->bpw = bpw; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return dln2_spi_enable(dln2, true); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int dln2_spi_transfer_one(struct spi_controller *host, 65462306a36Sopenharmony_ci struct spi_device *spi, 65562306a36Sopenharmony_ci struct spi_transfer *xfer) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 65862306a36Sopenharmony_ci int status; 65962306a36Sopenharmony_ci u8 attr = 0; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci status = dln2_spi_transfer_setup(dln2, xfer->speed_hz, 66262306a36Sopenharmony_ci xfer->bits_per_word, 66362306a36Sopenharmony_ci spi->mode); 66462306a36Sopenharmony_ci if (status < 0) { 66562306a36Sopenharmony_ci dev_err(&dln2->pdev->dev, "Cannot setup transfer\n"); 66662306a36Sopenharmony_ci return status; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!xfer->cs_change && !spi_transfer_is_last(host, xfer)) 67062306a36Sopenharmony_ci attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci status = dln2_spi_rdwr(dln2, xfer->tx_buf, xfer->rx_buf, 67362306a36Sopenharmony_ci xfer->len, attr); 67462306a36Sopenharmony_ci if (status < 0) 67562306a36Sopenharmony_ci dev_err(&dln2->pdev->dev, "write/read failed!\n"); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return status; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int dln2_spi_probe(struct platform_device *pdev) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct spi_controller *host; 68362306a36Sopenharmony_ci struct dln2_spi *dln2; 68462306a36Sopenharmony_ci struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); 68562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 68662306a36Sopenharmony_ci int ret; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci host = spi_alloc_host(&pdev->dev, sizeof(*dln2)); 68962306a36Sopenharmony_ci if (!host) 69062306a36Sopenharmony_ci return -ENOMEM; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci device_set_node(&host->dev, dev_fwnode(dev)); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci dln2 = spi_controller_get_devdata(host); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); 69962306a36Sopenharmony_ci if (!dln2->buf) { 70062306a36Sopenharmony_ci ret = -ENOMEM; 70162306a36Sopenharmony_ci goto exit_free_host; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci dln2->host = host; 70562306a36Sopenharmony_ci dln2->pdev = pdev; 70662306a36Sopenharmony_ci dln2->port = pdata->port; 70762306a36Sopenharmony_ci /* cs/mode can never be 0xff, so the first transfer will set them */ 70862306a36Sopenharmony_ci dln2->cs = 0xff; 70962306a36Sopenharmony_ci dln2->mode = 0xff; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* disable SPI module before continuing with the setup */ 71262306a36Sopenharmony_ci ret = dln2_spi_enable(dln2, false); 71362306a36Sopenharmony_ci if (ret < 0) { 71462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to disable SPI module\n"); 71562306a36Sopenharmony_ci goto exit_free_host; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = dln2_spi_get_cs_num(dln2, &host->num_chipselect); 71962306a36Sopenharmony_ci if (ret < 0) { 72062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get number of CS pins\n"); 72162306a36Sopenharmony_ci goto exit_free_host; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ret = dln2_spi_get_speed_range(dln2, 72562306a36Sopenharmony_ci &host->min_speed_hz, 72662306a36Sopenharmony_ci &host->max_speed_hz); 72762306a36Sopenharmony_ci if (ret < 0) { 72862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); 72962306a36Sopenharmony_ci goto exit_free_host; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = dln2_spi_get_supported_frame_sizes(dln2, 73362306a36Sopenharmony_ci &host->bits_per_word_mask); 73462306a36Sopenharmony_ci if (ret < 0) { 73562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); 73662306a36Sopenharmony_ci goto exit_free_host; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ret = dln2_spi_cs_enable_all(dln2, true); 74062306a36Sopenharmony_ci if (ret < 0) { 74162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable CS pins\n"); 74262306a36Sopenharmony_ci goto exit_free_host; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci host->bus_num = -1; 74662306a36Sopenharmony_ci host->mode_bits = SPI_CPOL | SPI_CPHA; 74762306a36Sopenharmony_ci host->prepare_message = dln2_spi_prepare_message; 74862306a36Sopenharmony_ci host->transfer_one = dln2_spi_transfer_one; 74962306a36Sopenharmony_ci host->auto_runtime_pm = true; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* enable SPI module, we're good to go */ 75262306a36Sopenharmony_ci ret = dln2_spi_enable(dln2, true); 75362306a36Sopenharmony_ci if (ret < 0) { 75462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable SPI module\n"); 75562306a36Sopenharmony_ci goto exit_free_host; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 75962306a36Sopenharmony_ci DLN2_RPM_AUTOSUSPEND_TIMEOUT); 76062306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 76162306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 76262306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci ret = devm_spi_register_controller(&pdev->dev, host); 76562306a36Sopenharmony_ci if (ret < 0) { 76662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register host\n"); 76762306a36Sopenharmony_ci goto exit_register; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ciexit_register: 77362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 77462306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (dln2_spi_enable(dln2, false) < 0) 77762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to disable SPI module\n"); 77862306a36Sopenharmony_ciexit_free_host: 77962306a36Sopenharmony_ci spi_controller_put(host); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return ret; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_cistatic void dln2_spi_remove(struct platform_device *pdev) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct spi_controller *host = platform_get_drvdata(pdev); 78762306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (dln2_spi_enable(dln2, false) < 0) 79262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to disable SPI module\n"); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 79662306a36Sopenharmony_cistatic int dln2_spi_suspend(struct device *dev) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci int ret; 79962306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 80062306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ret = spi_controller_suspend(host); 80362306a36Sopenharmony_ci if (ret < 0) 80462306a36Sopenharmony_ci return ret; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 80762306a36Sopenharmony_ci ret = dln2_spi_enable(dln2, false); 80862306a36Sopenharmony_ci if (ret < 0) 80962306a36Sopenharmony_ci return ret; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* 81362306a36Sopenharmony_ci * USB power may be cut off during sleep. Resetting the following 81462306a36Sopenharmony_ci * parameters will force the board to be set up before first transfer. 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci dln2->cs = 0xff; 81762306a36Sopenharmony_ci dln2->speed = 0; 81862306a36Sopenharmony_ci dln2->bpw = 0; 81962306a36Sopenharmony_ci dln2->mode = 0xff; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int dln2_spi_resume(struct device *dev) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 82862306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 83162306a36Sopenharmony_ci ret = dln2_spi_cs_enable_all(dln2, true); 83262306a36Sopenharmony_ci if (ret < 0) 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ret = dln2_spi_enable(dln2, true); 83662306a36Sopenharmony_ci if (ret < 0) 83762306a36Sopenharmony_ci return ret; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return spi_controller_resume(host); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci#ifdef CONFIG_PM 84562306a36Sopenharmony_cistatic int dln2_spi_runtime_suspend(struct device *dev) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 84862306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return dln2_spi_enable(dln2, false); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic int dln2_spi_runtime_resume(struct device *dev) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 85662306a36Sopenharmony_ci struct dln2_spi *dln2 = spi_controller_get_devdata(host); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return dln2_spi_enable(dln2, true); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci#endif /* CONFIG_PM */ 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct dev_pm_ops dln2_spi_pm = { 86362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume) 86462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend, 86562306a36Sopenharmony_ci dln2_spi_runtime_resume, NULL) 86662306a36Sopenharmony_ci}; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic struct platform_driver spi_dln2_driver = { 86962306a36Sopenharmony_ci .driver = { 87062306a36Sopenharmony_ci .name = "dln2-spi", 87162306a36Sopenharmony_ci .pm = &dln2_spi_pm, 87262306a36Sopenharmony_ci }, 87362306a36Sopenharmony_ci .probe = dln2_spi_probe, 87462306a36Sopenharmony_ci .remove_new = dln2_spi_remove, 87562306a36Sopenharmony_ci}; 87662306a36Sopenharmony_cimodule_platform_driver(spi_dln2_driver); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Diolan DLN2 SPI host interface"); 87962306a36Sopenharmony_ciMODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 88062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 88162306a36Sopenharmony_ciMODULE_ALIAS("platform:dln2-spi"); 882