13d8c0d74SLaurentiu Palcu /* 23d8c0d74SLaurentiu Palcu * Driver for the Diolan DLN-2 USB-SPI adapter 33d8c0d74SLaurentiu Palcu * 43d8c0d74SLaurentiu Palcu * Copyright (c) 2014 Intel Corporation 53d8c0d74SLaurentiu Palcu * 63d8c0d74SLaurentiu Palcu * This program is free software; you can redistribute it and/or 73d8c0d74SLaurentiu Palcu * modify it under the terms of the GNU General Public License as 83d8c0d74SLaurentiu Palcu * published by the Free Software Foundation, version 2. 93d8c0d74SLaurentiu Palcu */ 103d8c0d74SLaurentiu Palcu 113d8c0d74SLaurentiu Palcu #include <linux/kernel.h> 123d8c0d74SLaurentiu Palcu #include <linux/module.h> 133d8c0d74SLaurentiu Palcu #include <linux/platform_device.h> 143d8c0d74SLaurentiu Palcu #include <linux/mfd/dln2.h> 153d8c0d74SLaurentiu Palcu #include <linux/spi/spi.h> 163d8c0d74SLaurentiu Palcu #include <linux/pm_runtime.h> 173d8c0d74SLaurentiu Palcu #include <asm/unaligned.h> 183d8c0d74SLaurentiu Palcu 193d8c0d74SLaurentiu Palcu #define DLN2_SPI_MODULE_ID 0x02 203d8c0d74SLaurentiu Palcu #define DLN2_SPI_CMD(cmd) DLN2_CMD(cmd, DLN2_SPI_MODULE_ID) 213d8c0d74SLaurentiu Palcu 223d8c0d74SLaurentiu Palcu /* SPI commands */ 233d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_PORT_COUNT DLN2_SPI_CMD(0x00) 243d8c0d74SLaurentiu Palcu #define DLN2_SPI_ENABLE DLN2_SPI_CMD(0x11) 253d8c0d74SLaurentiu Palcu #define DLN2_SPI_DISABLE DLN2_SPI_CMD(0x12) 263d8c0d74SLaurentiu Palcu #define DLN2_SPI_IS_ENABLED DLN2_SPI_CMD(0x13) 273d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_MODE DLN2_SPI_CMD(0x14) 283d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MODE DLN2_SPI_CMD(0x15) 293d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_FRAME_SIZE DLN2_SPI_CMD(0x16) 303d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_FRAME_SIZE DLN2_SPI_CMD(0x17) 313d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_FREQUENCY DLN2_SPI_CMD(0x18) 323d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_FREQUENCY DLN2_SPI_CMD(0x19) 333d8c0d74SLaurentiu Palcu #define DLN2_SPI_READ_WRITE DLN2_SPI_CMD(0x1A) 343d8c0d74SLaurentiu Palcu #define DLN2_SPI_READ DLN2_SPI_CMD(0x1B) 353d8c0d74SLaurentiu Palcu #define DLN2_SPI_WRITE DLN2_SPI_CMD(0x1C) 363d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x20) 373d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x21) 383d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_AFTER_SS DLN2_SPI_CMD(0x22) 393d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_AFTER_SS DLN2_SPI_CMD(0x23) 403d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x24) 413d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x25) 423d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_SS DLN2_SPI_CMD(0x26) 433d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SS DLN2_SPI_CMD(0x27) 443d8c0d74SLaurentiu Palcu #define DLN2_SPI_RELEASE_SS DLN2_SPI_CMD(0x28) 453d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_ENABLE DLN2_SPI_CMD(0x2B) 463d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_DISABLE DLN2_SPI_CMD(0x2C) 473d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_IS_ENABLED DLN2_SPI_CMD(0x2D) 483d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_ENABLE DLN2_SPI_CMD(0x2E) 493d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_DISABLE DLN2_SPI_CMD(0x2F) 503d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_IS_ENABLED DLN2_SPI_CMD(0x30) 513d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_ENABLE DLN2_SPI_CMD(0x31) 523d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_DISABLE DLN2_SPI_CMD(0x32) 533d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_IS_ENABLED DLN2_SPI_CMD(0x33) 543d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_CPHA DLN2_SPI_CMD(0x34) 553d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_CPHA DLN2_SPI_CMD(0x35) 563d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_CPOL DLN2_SPI_CMD(0x36) 573d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_CPOL DLN2_SPI_CMD(0x37) 583d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_ENABLE DLN2_SPI_CMD(0x38) 593d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_DISABLE DLN2_SPI_CMD(0x39) 603d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_IS_ENABLED DLN2_SPI_CMD(0x3A) 613d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_MODES DLN2_SPI_CMD(0x40) 623d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_CPHA_VALUES DLN2_SPI_CMD(0x41) 633d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_CPOL_VALUES DLN2_SPI_CMD(0x42) 643d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_FRAME_SIZES DLN2_SPI_CMD(0x43) 653d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SS_COUNT DLN2_SPI_CMD(0x44) 663d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_FREQUENCY DLN2_SPI_CMD(0x45) 673d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_FREQUENCY DLN2_SPI_CMD(0x46) 683d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x47) 693d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x48) 703d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_AFTER_SS DLN2_SPI_CMD(0x49) 713d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_AFTER_SS DLN2_SPI_CMD(0x4A) 723d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4B) 733d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4C) 743d8c0d74SLaurentiu Palcu 753d8c0d74SLaurentiu Palcu #define DLN2_SPI_MAX_XFER_SIZE 256 763d8c0d74SLaurentiu Palcu #define DLN2_SPI_BUF_SIZE (DLN2_SPI_MAX_XFER_SIZE + 16) 773d8c0d74SLaurentiu Palcu #define DLN2_SPI_ATTR_LEAVE_SS_LOW BIT(0) 783d8c0d74SLaurentiu Palcu #define DLN2_TRANSFERS_WAIT_COMPLETE 1 793d8c0d74SLaurentiu Palcu #define DLN2_TRANSFERS_CANCEL 0 803d8c0d74SLaurentiu Palcu #define DLN2_RPM_AUTOSUSPEND_TIMEOUT 2000 813d8c0d74SLaurentiu Palcu 823d8c0d74SLaurentiu Palcu struct dln2_spi { 833d8c0d74SLaurentiu Palcu struct platform_device *pdev; 843d8c0d74SLaurentiu Palcu struct spi_master *master; 853d8c0d74SLaurentiu Palcu u8 port; 863d8c0d74SLaurentiu Palcu 873d8c0d74SLaurentiu Palcu /* 883d8c0d74SLaurentiu Palcu * This buffer will be used mainly for read/write operations. Since 893d8c0d74SLaurentiu Palcu * they're quite large, we cannot use the stack. Protection is not 903d8c0d74SLaurentiu Palcu * needed because all SPI communication is serialized by the SPI core. 913d8c0d74SLaurentiu Palcu */ 923d8c0d74SLaurentiu Palcu void *buf; 933d8c0d74SLaurentiu Palcu 943d8c0d74SLaurentiu Palcu u8 bpw; 953d8c0d74SLaurentiu Palcu u32 speed; 963d8c0d74SLaurentiu Palcu u16 mode; 973d8c0d74SLaurentiu Palcu u8 cs; 983d8c0d74SLaurentiu Palcu }; 993d8c0d74SLaurentiu Palcu 1003d8c0d74SLaurentiu Palcu /* 1013d8c0d74SLaurentiu Palcu * Enable/Disable SPI module. The disable command will wait for transfers to 1023d8c0d74SLaurentiu Palcu * complete first. 1033d8c0d74SLaurentiu Palcu */ 1043d8c0d74SLaurentiu Palcu static int dln2_spi_enable(struct dln2_spi *dln2, bool enable) 1053d8c0d74SLaurentiu Palcu { 1063d8c0d74SLaurentiu Palcu u16 cmd; 1073d8c0d74SLaurentiu Palcu struct { 1083d8c0d74SLaurentiu Palcu u8 port; 1093d8c0d74SLaurentiu Palcu u8 wait_for_completion; 1103d8c0d74SLaurentiu Palcu } tx; 1113d8c0d74SLaurentiu Palcu unsigned len = sizeof(tx); 1123d8c0d74SLaurentiu Palcu 1133d8c0d74SLaurentiu Palcu tx.port = dln2->port; 1143d8c0d74SLaurentiu Palcu 1153d8c0d74SLaurentiu Palcu if (enable) { 1163d8c0d74SLaurentiu Palcu cmd = DLN2_SPI_ENABLE; 1173d8c0d74SLaurentiu Palcu len -= sizeof(tx.wait_for_completion); 1183d8c0d74SLaurentiu Palcu } else { 1193d8c0d74SLaurentiu Palcu tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE; 1203d8c0d74SLaurentiu Palcu cmd = DLN2_SPI_DISABLE; 1213d8c0d74SLaurentiu Palcu } 1223d8c0d74SLaurentiu Palcu 1232b5e368eSLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, cmd, &tx, len); 1243d8c0d74SLaurentiu Palcu } 1253d8c0d74SLaurentiu Palcu 1263d8c0d74SLaurentiu Palcu /* 1273d8c0d74SLaurentiu Palcu * Select/unselect multiple CS lines. The selected lines will be automatically 1283d8c0d74SLaurentiu Palcu * toggled LOW/HIGH by the board firmware during transfers, provided they're 1293d8c0d74SLaurentiu Palcu * enabled first. 1303d8c0d74SLaurentiu Palcu * 1313d8c0d74SLaurentiu Palcu * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation 1323d8c0d74SLaurentiu Palcu * will toggle the lines LOW/HIGH automatically. 1333d8c0d74SLaurentiu Palcu */ 1343d8c0d74SLaurentiu Palcu static int dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask) 1353d8c0d74SLaurentiu Palcu { 1363d8c0d74SLaurentiu Palcu struct { 1373d8c0d74SLaurentiu Palcu u8 port; 1383d8c0d74SLaurentiu Palcu u8 cs; 1393d8c0d74SLaurentiu Palcu } tx; 1403d8c0d74SLaurentiu Palcu 1413d8c0d74SLaurentiu Palcu tx.port = dln2->port; 1423d8c0d74SLaurentiu Palcu 1433d8c0d74SLaurentiu Palcu /* 1443d8c0d74SLaurentiu Palcu * According to Diolan docs, "a slave device can be selected by changing 1453d8c0d74SLaurentiu Palcu * the corresponding bit value to 0". The rest must be set to 1. Hence 1463d8c0d74SLaurentiu Palcu * the bitwise NOT in front. 1473d8c0d74SLaurentiu Palcu */ 1483d8c0d74SLaurentiu Palcu tx.cs = ~cs_mask; 1493d8c0d74SLaurentiu Palcu 1503d8c0d74SLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_SS, &tx, sizeof(tx)); 1513d8c0d74SLaurentiu Palcu } 1523d8c0d74SLaurentiu Palcu 1533d8c0d74SLaurentiu Palcu /* 1543d8c0d74SLaurentiu Palcu * Select one CS line. The other lines will be un-selected. 1553d8c0d74SLaurentiu Palcu */ 1563d8c0d74SLaurentiu Palcu static int dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs) 1573d8c0d74SLaurentiu Palcu { 1583d8c0d74SLaurentiu Palcu return dln2_spi_cs_set(dln2, BIT(cs)); 1593d8c0d74SLaurentiu Palcu } 1603d8c0d74SLaurentiu Palcu 1613d8c0d74SLaurentiu Palcu /* 1623d8c0d74SLaurentiu Palcu * Enable/disable CS lines for usage. The module has to be disabled first. 1633d8c0d74SLaurentiu Palcu */ 1643d8c0d74SLaurentiu Palcu static int dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable) 1653d8c0d74SLaurentiu Palcu { 1663d8c0d74SLaurentiu Palcu struct { 1673d8c0d74SLaurentiu Palcu u8 port; 1683d8c0d74SLaurentiu Palcu u8 cs; 1693d8c0d74SLaurentiu Palcu } tx; 1703d8c0d74SLaurentiu Palcu u16 cmd; 1713d8c0d74SLaurentiu Palcu 1723d8c0d74SLaurentiu Palcu tx.port = dln2->port; 1733d8c0d74SLaurentiu Palcu tx.cs = cs_mask; 1743d8c0d74SLaurentiu Palcu cmd = enable ? DLN2_SPI_SS_MULTI_ENABLE : DLN2_SPI_SS_MULTI_DISABLE; 1753d8c0d74SLaurentiu Palcu 1763d8c0d74SLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); 1773d8c0d74SLaurentiu Palcu } 1783d8c0d74SLaurentiu Palcu 1793d8c0d74SLaurentiu Palcu static int dln2_spi_cs_enable_all(struct dln2_spi *dln2, bool enable) 1803d8c0d74SLaurentiu Palcu { 1813d8c0d74SLaurentiu Palcu u8 cs_mask = GENMASK(dln2->master->num_chipselect - 1, 0); 1823d8c0d74SLaurentiu Palcu 1833d8c0d74SLaurentiu Palcu return dln2_spi_cs_enable(dln2, cs_mask, enable); 1843d8c0d74SLaurentiu Palcu } 1853d8c0d74SLaurentiu Palcu 1863d8c0d74SLaurentiu Palcu static int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num) 1873d8c0d74SLaurentiu Palcu { 1883d8c0d74SLaurentiu Palcu int ret; 1893d8c0d74SLaurentiu Palcu struct { 1903d8c0d74SLaurentiu Palcu u8 port; 1913d8c0d74SLaurentiu Palcu } tx; 1923d8c0d74SLaurentiu Palcu struct { 1933d8c0d74SLaurentiu Palcu __le16 cs_count; 1943d8c0d74SLaurentiu Palcu } rx; 1953d8c0d74SLaurentiu Palcu unsigned rx_len = sizeof(rx); 1963d8c0d74SLaurentiu Palcu 1973d8c0d74SLaurentiu Palcu tx.port = dln2->port; 1983d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SS_COUNT, &tx, sizeof(tx), 1993d8c0d74SLaurentiu Palcu &rx, &rx_len); 2003d8c0d74SLaurentiu Palcu if (ret < 0) 2013d8c0d74SLaurentiu Palcu return ret; 2023d8c0d74SLaurentiu Palcu if (rx_len < sizeof(rx)) 2033d8c0d74SLaurentiu Palcu return -EPROTO; 2043d8c0d74SLaurentiu Palcu 2053d8c0d74SLaurentiu Palcu *cs_num = le16_to_cpu(rx.cs_count); 2063d8c0d74SLaurentiu Palcu 2073d8c0d74SLaurentiu Palcu dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num); 2083d8c0d74SLaurentiu Palcu 2093d8c0d74SLaurentiu Palcu return 0; 2103d8c0d74SLaurentiu Palcu } 2113d8c0d74SLaurentiu Palcu 2123d8c0d74SLaurentiu Palcu static int dln2_spi_get_speed(struct dln2_spi *dln2, u16 cmd, u32 *freq) 2133d8c0d74SLaurentiu Palcu { 2143d8c0d74SLaurentiu Palcu int ret; 2153d8c0d74SLaurentiu Palcu struct { 2163d8c0d74SLaurentiu Palcu u8 port; 2173d8c0d74SLaurentiu Palcu } tx; 2183d8c0d74SLaurentiu Palcu struct { 2193d8c0d74SLaurentiu Palcu __le32 speed; 2203d8c0d74SLaurentiu Palcu } rx; 2213d8c0d74SLaurentiu Palcu unsigned rx_len = sizeof(rx); 2223d8c0d74SLaurentiu Palcu 2233d8c0d74SLaurentiu Palcu tx.port = dln2->port; 2243d8c0d74SLaurentiu Palcu 2253d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, cmd, &tx, sizeof(tx), &rx, &rx_len); 2263d8c0d74SLaurentiu Palcu if (ret < 0) 2273d8c0d74SLaurentiu Palcu return ret; 2283d8c0d74SLaurentiu Palcu if (rx_len < sizeof(rx)) 2293d8c0d74SLaurentiu Palcu return -EPROTO; 2303d8c0d74SLaurentiu Palcu 2313d8c0d74SLaurentiu Palcu *freq = le32_to_cpu(rx.speed); 2323d8c0d74SLaurentiu Palcu 2333d8c0d74SLaurentiu Palcu return 0; 2343d8c0d74SLaurentiu Palcu } 2353d8c0d74SLaurentiu Palcu 2363d8c0d74SLaurentiu Palcu /* 2373d8c0d74SLaurentiu Palcu * Get bus min/max frequencies. 2383d8c0d74SLaurentiu Palcu */ 2393d8c0d74SLaurentiu Palcu static int dln2_spi_get_speed_range(struct dln2_spi *dln2, u32 *fmin, u32 *fmax) 2403d8c0d74SLaurentiu Palcu { 2413d8c0d74SLaurentiu Palcu int ret; 2423d8c0d74SLaurentiu Palcu 2433d8c0d74SLaurentiu Palcu ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MIN_FREQUENCY, fmin); 2443d8c0d74SLaurentiu Palcu if (ret < 0) 2453d8c0d74SLaurentiu Palcu return ret; 2463d8c0d74SLaurentiu Palcu 2473d8c0d74SLaurentiu Palcu ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MAX_FREQUENCY, fmax); 2483d8c0d74SLaurentiu Palcu if (ret < 0) 2493d8c0d74SLaurentiu Palcu return ret; 2503d8c0d74SLaurentiu Palcu 2513d8c0d74SLaurentiu Palcu dev_dbg(&dln2->pdev->dev, "freq_min = %d, freq_max = %d\n", 2523d8c0d74SLaurentiu Palcu *fmin, *fmax); 2533d8c0d74SLaurentiu Palcu 2543d8c0d74SLaurentiu Palcu return 0; 2553d8c0d74SLaurentiu Palcu } 2563d8c0d74SLaurentiu Palcu 2573d8c0d74SLaurentiu Palcu /* 2583d8c0d74SLaurentiu Palcu * Set the bus speed. The module will automatically round down to the closest 2593d8c0d74SLaurentiu Palcu * available frequency and returns it. The module has to be disabled first. 2603d8c0d74SLaurentiu Palcu */ 2613d8c0d74SLaurentiu Palcu static int dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed) 2623d8c0d74SLaurentiu Palcu { 2633d8c0d74SLaurentiu Palcu int ret; 2643d8c0d74SLaurentiu Palcu struct { 2653d8c0d74SLaurentiu Palcu u8 port; 2663d8c0d74SLaurentiu Palcu __le32 speed; 2673d8c0d74SLaurentiu Palcu } __packed tx; 2683d8c0d74SLaurentiu Palcu struct { 2693d8c0d74SLaurentiu Palcu __le32 speed; 2703d8c0d74SLaurentiu Palcu } rx; 2713d8c0d74SLaurentiu Palcu int rx_len = sizeof(rx); 2723d8c0d74SLaurentiu Palcu 2733d8c0d74SLaurentiu Palcu tx.port = dln2->port; 2743d8c0d74SLaurentiu Palcu tx.speed = cpu_to_le32(speed); 2753d8c0d74SLaurentiu Palcu 2763d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, DLN2_SPI_SET_FREQUENCY, &tx, sizeof(tx), 2773d8c0d74SLaurentiu Palcu &rx, &rx_len); 2783d8c0d74SLaurentiu Palcu if (ret < 0) 2793d8c0d74SLaurentiu Palcu return ret; 2803d8c0d74SLaurentiu Palcu if (rx_len < sizeof(rx)) 2813d8c0d74SLaurentiu Palcu return -EPROTO; 2823d8c0d74SLaurentiu Palcu 2833d8c0d74SLaurentiu Palcu return 0; 2843d8c0d74SLaurentiu Palcu } 2853d8c0d74SLaurentiu Palcu 2863d8c0d74SLaurentiu Palcu /* 2873d8c0d74SLaurentiu Palcu * Change CPOL & CPHA. The module has to be disabled first. 2883d8c0d74SLaurentiu Palcu */ 2893d8c0d74SLaurentiu Palcu static int dln2_spi_set_mode(struct dln2_spi *dln2, u8 mode) 2903d8c0d74SLaurentiu Palcu { 2913d8c0d74SLaurentiu Palcu struct { 2923d8c0d74SLaurentiu Palcu u8 port; 2933d8c0d74SLaurentiu Palcu u8 mode; 2943d8c0d74SLaurentiu Palcu } tx; 2953d8c0d74SLaurentiu Palcu 2963d8c0d74SLaurentiu Palcu tx.port = dln2->port; 2973d8c0d74SLaurentiu Palcu tx.mode = mode; 2983d8c0d74SLaurentiu Palcu 2993d8c0d74SLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_MODE, &tx, sizeof(tx)); 3003d8c0d74SLaurentiu Palcu } 3013d8c0d74SLaurentiu Palcu 3023d8c0d74SLaurentiu Palcu /* 3033d8c0d74SLaurentiu Palcu * Change frame size. The module has to be disabled first. 3043d8c0d74SLaurentiu Palcu */ 3053d8c0d74SLaurentiu Palcu static int dln2_spi_set_bpw(struct dln2_spi *dln2, u8 bpw) 3063d8c0d74SLaurentiu Palcu { 3073d8c0d74SLaurentiu Palcu struct { 3083d8c0d74SLaurentiu Palcu u8 port; 3093d8c0d74SLaurentiu Palcu u8 bpw; 3103d8c0d74SLaurentiu Palcu } tx; 3113d8c0d74SLaurentiu Palcu 3123d8c0d74SLaurentiu Palcu tx.port = dln2->port; 3133d8c0d74SLaurentiu Palcu tx.bpw = bpw; 3143d8c0d74SLaurentiu Palcu 3153d8c0d74SLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_FRAME_SIZE, 3163d8c0d74SLaurentiu Palcu &tx, sizeof(tx)); 3173d8c0d74SLaurentiu Palcu } 3183d8c0d74SLaurentiu Palcu 3193d8c0d74SLaurentiu Palcu static int dln2_spi_get_supported_frame_sizes(struct dln2_spi *dln2, 3203d8c0d74SLaurentiu Palcu u32 *bpw_mask) 3213d8c0d74SLaurentiu Palcu { 3223d8c0d74SLaurentiu Palcu int ret; 3233d8c0d74SLaurentiu Palcu struct { 3243d8c0d74SLaurentiu Palcu u8 port; 3253d8c0d74SLaurentiu Palcu } tx; 3263d8c0d74SLaurentiu Palcu struct { 3273d8c0d74SLaurentiu Palcu u8 count; 3283d8c0d74SLaurentiu Palcu u8 frame_sizes[36]; 3293d8c0d74SLaurentiu Palcu } *rx = dln2->buf; 3303d8c0d74SLaurentiu Palcu unsigned rx_len = sizeof(*rx); 3313d8c0d74SLaurentiu Palcu int i; 3323d8c0d74SLaurentiu Palcu 3333d8c0d74SLaurentiu Palcu tx.port = dln2->port; 3343d8c0d74SLaurentiu Palcu 3353d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SUPPORTED_FRAME_SIZES, 3363d8c0d74SLaurentiu Palcu &tx, sizeof(tx), rx, &rx_len); 3373d8c0d74SLaurentiu Palcu if (ret < 0) 3383d8c0d74SLaurentiu Palcu return ret; 3393d8c0d74SLaurentiu Palcu if (rx_len < sizeof(*rx)) 3403d8c0d74SLaurentiu Palcu return -EPROTO; 3413d8c0d74SLaurentiu Palcu if (rx->count > ARRAY_SIZE(rx->frame_sizes)) 3423d8c0d74SLaurentiu Palcu return -EPROTO; 3433d8c0d74SLaurentiu Palcu 3443d8c0d74SLaurentiu Palcu *bpw_mask = 0; 3453d8c0d74SLaurentiu Palcu for (i = 0; i < rx->count; i++) 3463d8c0d74SLaurentiu Palcu *bpw_mask |= BIT(rx->frame_sizes[i] - 1); 3473d8c0d74SLaurentiu Palcu 3483d8c0d74SLaurentiu Palcu dev_dbg(&dln2->pdev->dev, "bpw_mask = 0x%X\n", *bpw_mask); 3493d8c0d74SLaurentiu Palcu 3503d8c0d74SLaurentiu Palcu return 0; 3513d8c0d74SLaurentiu Palcu } 3523d8c0d74SLaurentiu Palcu 3533d8c0d74SLaurentiu Palcu /* 3543d8c0d74SLaurentiu Palcu * Copy the data to DLN2 buffer and change the byte order to LE, requested by 3553d8c0d74SLaurentiu Palcu * DLN2 module. SPI core makes sure that the data length is a multiple of word 3563d8c0d74SLaurentiu Palcu * size. 3573d8c0d74SLaurentiu Palcu */ 3583d8c0d74SLaurentiu Palcu static int dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw) 3593d8c0d74SLaurentiu Palcu { 3603d8c0d74SLaurentiu Palcu #ifdef __LITTLE_ENDIAN 3613d8c0d74SLaurentiu Palcu memcpy(dln2_buf, src, len); 3623d8c0d74SLaurentiu Palcu #else 3633d8c0d74SLaurentiu Palcu if (bpw <= 8) { 3643d8c0d74SLaurentiu Palcu memcpy(dln2_buf, src, len); 3653d8c0d74SLaurentiu Palcu } else if (bpw <= 16) { 3663d8c0d74SLaurentiu Palcu __le16 *d = (__le16 *)dln2_buf; 3673d8c0d74SLaurentiu Palcu u16 *s = (u16 *)src; 3683d8c0d74SLaurentiu Palcu 3693d8c0d74SLaurentiu Palcu len = len / 2; 3703d8c0d74SLaurentiu Palcu while (len--) 3713d8c0d74SLaurentiu Palcu *d++ = cpu_to_le16p(s++); 3723d8c0d74SLaurentiu Palcu } else { 3733d8c0d74SLaurentiu Palcu __le32 *d = (__le32 *)dln2_buf; 3743d8c0d74SLaurentiu Palcu u32 *s = (u32 *)src; 3753d8c0d74SLaurentiu Palcu 3763d8c0d74SLaurentiu Palcu len = len / 4; 3773d8c0d74SLaurentiu Palcu while (len--) 3783d8c0d74SLaurentiu Palcu *d++ = cpu_to_le32p(s++); 3793d8c0d74SLaurentiu Palcu } 3803d8c0d74SLaurentiu Palcu #endif 3813d8c0d74SLaurentiu Palcu 3823d8c0d74SLaurentiu Palcu return 0; 3833d8c0d74SLaurentiu Palcu } 3843d8c0d74SLaurentiu Palcu 3853d8c0d74SLaurentiu Palcu /* 3863d8c0d74SLaurentiu Palcu * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2 3873d8c0d74SLaurentiu Palcu * buffer is LE ordered. SPI core makes sure that the data length is a multiple 3883d8c0d74SLaurentiu Palcu * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make 3893d8c0d74SLaurentiu Palcu * sure we avoid unaligned accesses for 32 bit case. 3903d8c0d74SLaurentiu Palcu */ 3913d8c0d74SLaurentiu Palcu static int dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw) 3923d8c0d74SLaurentiu Palcu { 3933d8c0d74SLaurentiu Palcu #ifdef __LITTLE_ENDIAN 3943d8c0d74SLaurentiu Palcu memcpy(dest, dln2_buf, len); 3953d8c0d74SLaurentiu Palcu #else 3963d8c0d74SLaurentiu Palcu if (bpw <= 8) { 3973d8c0d74SLaurentiu Palcu memcpy(dest, dln2_buf, len); 3983d8c0d74SLaurentiu Palcu } else if (bpw <= 16) { 3993d8c0d74SLaurentiu Palcu u16 *d = (u16 *)dest; 4003d8c0d74SLaurentiu Palcu __le16 *s = (__le16 *)dln2_buf; 4013d8c0d74SLaurentiu Palcu 4023d8c0d74SLaurentiu Palcu len = len / 2; 4033d8c0d74SLaurentiu Palcu while (len--) 4043d8c0d74SLaurentiu Palcu *d++ = le16_to_cpup(s++); 4053d8c0d74SLaurentiu Palcu } else { 4063d8c0d74SLaurentiu Palcu u32 *d = (u32 *)dest; 4073d8c0d74SLaurentiu Palcu __le32 *s = (__le32 *)dln2_buf; 4083d8c0d74SLaurentiu Palcu 4093d8c0d74SLaurentiu Palcu len = len / 4; 4103d8c0d74SLaurentiu Palcu while (len--) 4113d8c0d74SLaurentiu Palcu *d++ = get_unaligned_le32(s++); 4123d8c0d74SLaurentiu Palcu } 4133d8c0d74SLaurentiu Palcu #endif 4143d8c0d74SLaurentiu Palcu 4153d8c0d74SLaurentiu Palcu return 0; 4163d8c0d74SLaurentiu Palcu } 4173d8c0d74SLaurentiu Palcu 4183d8c0d74SLaurentiu Palcu /* 4193d8c0d74SLaurentiu Palcu * Perform one write operation. 4203d8c0d74SLaurentiu Palcu */ 4213d8c0d74SLaurentiu Palcu static int dln2_spi_write_one(struct dln2_spi *dln2, const u8 *data, 4223d8c0d74SLaurentiu Palcu u16 data_len, u8 attr) 4233d8c0d74SLaurentiu Palcu { 4243d8c0d74SLaurentiu Palcu struct { 4253d8c0d74SLaurentiu Palcu u8 port; 4263d8c0d74SLaurentiu Palcu __le16 size; 4273d8c0d74SLaurentiu Palcu u8 attr; 4283d8c0d74SLaurentiu Palcu u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 4293d8c0d74SLaurentiu Palcu } __packed *tx = dln2->buf; 4303d8c0d74SLaurentiu Palcu unsigned tx_len; 4313d8c0d74SLaurentiu Palcu 4323d8c0d74SLaurentiu Palcu BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE); 4333d8c0d74SLaurentiu Palcu 4343d8c0d74SLaurentiu Palcu if (data_len > DLN2_SPI_MAX_XFER_SIZE) 4353d8c0d74SLaurentiu Palcu return -EINVAL; 4363d8c0d74SLaurentiu Palcu 4373d8c0d74SLaurentiu Palcu tx->port = dln2->port; 4383d8c0d74SLaurentiu Palcu tx->size = cpu_to_le16(data_len); 4393d8c0d74SLaurentiu Palcu tx->attr = attr; 4403d8c0d74SLaurentiu Palcu 4413d8c0d74SLaurentiu Palcu dln2_spi_copy_to_buf(tx->buf, data, data_len, dln2->bpw); 4423d8c0d74SLaurentiu Palcu 4433d8c0d74SLaurentiu Palcu tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; 4443d8c0d74SLaurentiu Palcu return dln2_transfer_tx(dln2->pdev, DLN2_SPI_WRITE, tx, tx_len); 4453d8c0d74SLaurentiu Palcu } 4463d8c0d74SLaurentiu Palcu 4473d8c0d74SLaurentiu Palcu /* 4483d8c0d74SLaurentiu Palcu * Perform one read operation. 4493d8c0d74SLaurentiu Palcu */ 4503d8c0d74SLaurentiu Palcu static int dln2_spi_read_one(struct dln2_spi *dln2, u8 *data, 4513d8c0d74SLaurentiu Palcu u16 data_len, u8 attr) 4523d8c0d74SLaurentiu Palcu { 4533d8c0d74SLaurentiu Palcu int ret; 4543d8c0d74SLaurentiu Palcu struct { 4553d8c0d74SLaurentiu Palcu u8 port; 4563d8c0d74SLaurentiu Palcu __le16 size; 4573d8c0d74SLaurentiu Palcu u8 attr; 4583d8c0d74SLaurentiu Palcu } __packed tx; 4593d8c0d74SLaurentiu Palcu struct { 4603d8c0d74SLaurentiu Palcu __le16 size; 4613d8c0d74SLaurentiu Palcu u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 4623d8c0d74SLaurentiu Palcu } __packed *rx = dln2->buf; 4633d8c0d74SLaurentiu Palcu unsigned rx_len = sizeof(*rx); 4643d8c0d74SLaurentiu Palcu 4653d8c0d74SLaurentiu Palcu BUILD_BUG_ON(sizeof(*rx) > DLN2_SPI_BUF_SIZE); 4663d8c0d74SLaurentiu Palcu 4673d8c0d74SLaurentiu Palcu if (data_len > DLN2_SPI_MAX_XFER_SIZE) 4683d8c0d74SLaurentiu Palcu return -EINVAL; 4693d8c0d74SLaurentiu Palcu 4703d8c0d74SLaurentiu Palcu tx.port = dln2->port; 4713d8c0d74SLaurentiu Palcu tx.size = cpu_to_le16(data_len); 4723d8c0d74SLaurentiu Palcu tx.attr = attr; 4733d8c0d74SLaurentiu Palcu 4743d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ, &tx, sizeof(tx), 4753d8c0d74SLaurentiu Palcu rx, &rx_len); 4763d8c0d74SLaurentiu Palcu if (ret < 0) 4773d8c0d74SLaurentiu Palcu return ret; 4783d8c0d74SLaurentiu Palcu if (rx_len < sizeof(rx->size) + data_len) 4793d8c0d74SLaurentiu Palcu return -EPROTO; 4803d8c0d74SLaurentiu Palcu if (le16_to_cpu(rx->size) != data_len) 4813d8c0d74SLaurentiu Palcu return -EPROTO; 4823d8c0d74SLaurentiu Palcu 4833d8c0d74SLaurentiu Palcu dln2_spi_copy_from_buf(data, rx->buf, data_len, dln2->bpw); 4843d8c0d74SLaurentiu Palcu 4853d8c0d74SLaurentiu Palcu return 0; 4863d8c0d74SLaurentiu Palcu } 4873d8c0d74SLaurentiu Palcu 4883d8c0d74SLaurentiu Palcu /* 4893d8c0d74SLaurentiu Palcu * Perform one write & read operation. 4903d8c0d74SLaurentiu Palcu */ 4913d8c0d74SLaurentiu Palcu static int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data, 4923d8c0d74SLaurentiu Palcu u8 *rx_data, u16 data_len, u8 attr) 4933d8c0d74SLaurentiu Palcu { 4943d8c0d74SLaurentiu Palcu int ret; 4953d8c0d74SLaurentiu Palcu struct { 4963d8c0d74SLaurentiu Palcu u8 port; 4973d8c0d74SLaurentiu Palcu __le16 size; 4983d8c0d74SLaurentiu Palcu u8 attr; 4993d8c0d74SLaurentiu Palcu u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 5003d8c0d74SLaurentiu Palcu } __packed *tx; 5013d8c0d74SLaurentiu Palcu struct { 5023d8c0d74SLaurentiu Palcu __le16 size; 5033d8c0d74SLaurentiu Palcu u8 buf[DLN2_SPI_MAX_XFER_SIZE]; 5043d8c0d74SLaurentiu Palcu } __packed *rx; 5053d8c0d74SLaurentiu Palcu unsigned tx_len, rx_len; 5063d8c0d74SLaurentiu Palcu 5073d8c0d74SLaurentiu Palcu BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE || 5083d8c0d74SLaurentiu Palcu sizeof(*rx) > DLN2_SPI_BUF_SIZE); 5093d8c0d74SLaurentiu Palcu 5103d8c0d74SLaurentiu Palcu if (data_len > DLN2_SPI_MAX_XFER_SIZE) 5113d8c0d74SLaurentiu Palcu return -EINVAL; 5123d8c0d74SLaurentiu Palcu 5133d8c0d74SLaurentiu Palcu /* 5143d8c0d74SLaurentiu Palcu * Since this is a pseudo full-duplex communication, we're perfectly 5153d8c0d74SLaurentiu Palcu * safe to use the same buffer for both tx and rx. When DLN2 sends the 5163d8c0d74SLaurentiu Palcu * response back, with the rx data, we don't need the tx buffer anymore. 5173d8c0d74SLaurentiu Palcu */ 5183d8c0d74SLaurentiu Palcu tx = dln2->buf; 5193d8c0d74SLaurentiu Palcu rx = dln2->buf; 5203d8c0d74SLaurentiu Palcu 5213d8c0d74SLaurentiu Palcu tx->port = dln2->port; 5223d8c0d74SLaurentiu Palcu tx->size = cpu_to_le16(data_len); 5233d8c0d74SLaurentiu Palcu tx->attr = attr; 5243d8c0d74SLaurentiu Palcu 5253d8c0d74SLaurentiu Palcu dln2_spi_copy_to_buf(tx->buf, tx_data, data_len, dln2->bpw); 5263d8c0d74SLaurentiu Palcu 5273d8c0d74SLaurentiu Palcu tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; 5283d8c0d74SLaurentiu Palcu rx_len = sizeof(*rx); 5293d8c0d74SLaurentiu Palcu 5303d8c0d74SLaurentiu Palcu ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ_WRITE, tx, tx_len, 5313d8c0d74SLaurentiu Palcu rx, &rx_len); 5323d8c0d74SLaurentiu Palcu if (ret < 0) 5333d8c0d74SLaurentiu Palcu return ret; 5343d8c0d74SLaurentiu Palcu if (rx_len < sizeof(rx->size) + data_len) 5353d8c0d74SLaurentiu Palcu return -EPROTO; 5363d8c0d74SLaurentiu Palcu if (le16_to_cpu(rx->size) != data_len) 5373d8c0d74SLaurentiu Palcu return -EPROTO; 5383d8c0d74SLaurentiu Palcu 5393d8c0d74SLaurentiu Palcu dln2_spi_copy_from_buf(rx_data, rx->buf, data_len, dln2->bpw); 5403d8c0d74SLaurentiu Palcu 5413d8c0d74SLaurentiu Palcu return 0; 5423d8c0d74SLaurentiu Palcu } 5433d8c0d74SLaurentiu Palcu 5443d8c0d74SLaurentiu Palcu /* 5453d8c0d74SLaurentiu Palcu * Read/Write wrapper. It will automatically split an operation into multiple 5463d8c0d74SLaurentiu Palcu * single ones due to device buffer constraints. 5473d8c0d74SLaurentiu Palcu */ 5483d8c0d74SLaurentiu Palcu static int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data, 5493d8c0d74SLaurentiu Palcu u8 *rx_data, u16 data_len, u8 attr) { 5503d8c0d74SLaurentiu Palcu int ret; 5513d8c0d74SLaurentiu Palcu u16 len; 5523d8c0d74SLaurentiu Palcu u8 temp_attr; 5533d8c0d74SLaurentiu Palcu u16 remaining = data_len; 5543d8c0d74SLaurentiu Palcu u16 offset; 5553d8c0d74SLaurentiu Palcu 5563d8c0d74SLaurentiu Palcu do { 5573d8c0d74SLaurentiu Palcu if (remaining > DLN2_SPI_MAX_XFER_SIZE) { 5583d8c0d74SLaurentiu Palcu len = DLN2_SPI_MAX_XFER_SIZE; 5593d8c0d74SLaurentiu Palcu temp_attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; 5603d8c0d74SLaurentiu Palcu } else { 5613d8c0d74SLaurentiu Palcu len = remaining; 5623d8c0d74SLaurentiu Palcu temp_attr = attr; 5633d8c0d74SLaurentiu Palcu } 5643d8c0d74SLaurentiu Palcu 5653d8c0d74SLaurentiu Palcu offset = data_len - remaining; 5663d8c0d74SLaurentiu Palcu 5673d8c0d74SLaurentiu Palcu if (tx_data && rx_data) { 5683d8c0d74SLaurentiu Palcu ret = dln2_spi_read_write_one(dln2, 5693d8c0d74SLaurentiu Palcu tx_data + offset, 5703d8c0d74SLaurentiu Palcu rx_data + offset, 5713d8c0d74SLaurentiu Palcu len, temp_attr); 5723d8c0d74SLaurentiu Palcu } else if (tx_data) { 5733d8c0d74SLaurentiu Palcu ret = dln2_spi_write_one(dln2, 5743d8c0d74SLaurentiu Palcu tx_data + offset, 5753d8c0d74SLaurentiu Palcu len, temp_attr); 5763d8c0d74SLaurentiu Palcu } else if (rx_data) { 5773d8c0d74SLaurentiu Palcu ret = dln2_spi_read_one(dln2, 5783d8c0d74SLaurentiu Palcu rx_data + offset, 5793d8c0d74SLaurentiu Palcu len, temp_attr); 5803d8c0d74SLaurentiu Palcu } else { 5813d8c0d74SLaurentiu Palcu return -EINVAL; 5823d8c0d74SLaurentiu Palcu } 5833d8c0d74SLaurentiu Palcu 5843d8c0d74SLaurentiu Palcu if (ret < 0) 5853d8c0d74SLaurentiu Palcu return ret; 5863d8c0d74SLaurentiu Palcu 5873d8c0d74SLaurentiu Palcu remaining -= len; 5883d8c0d74SLaurentiu Palcu } while (remaining); 5893d8c0d74SLaurentiu Palcu 5903d8c0d74SLaurentiu Palcu return 0; 5913d8c0d74SLaurentiu Palcu } 5923d8c0d74SLaurentiu Palcu 5933d8c0d74SLaurentiu Palcu static int dln2_spi_prepare_message(struct spi_master *master, 5943d8c0d74SLaurentiu Palcu struct spi_message *message) 5953d8c0d74SLaurentiu Palcu { 5963d8c0d74SLaurentiu Palcu int ret; 5973d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 5983d8c0d74SLaurentiu Palcu struct spi_device *spi = message->spi; 5993d8c0d74SLaurentiu Palcu 6003d8c0d74SLaurentiu Palcu if (dln2->cs != spi->chip_select) { 6013d8c0d74SLaurentiu Palcu ret = dln2_spi_cs_set_one(dln2, spi->chip_select); 6023d8c0d74SLaurentiu Palcu if (ret < 0) 6033d8c0d74SLaurentiu Palcu return ret; 6043d8c0d74SLaurentiu Palcu 6053d8c0d74SLaurentiu Palcu dln2->cs = spi->chip_select; 6063d8c0d74SLaurentiu Palcu } 6073d8c0d74SLaurentiu Palcu 6083d8c0d74SLaurentiu Palcu return 0; 6093d8c0d74SLaurentiu Palcu } 6103d8c0d74SLaurentiu Palcu 6113d8c0d74SLaurentiu Palcu static int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed, 6123d8c0d74SLaurentiu Palcu u8 bpw, u8 mode) 6133d8c0d74SLaurentiu Palcu { 6143d8c0d74SLaurentiu Palcu int ret; 6153d8c0d74SLaurentiu Palcu bool bus_setup_change; 6163d8c0d74SLaurentiu Palcu 6173d8c0d74SLaurentiu Palcu bus_setup_change = dln2->speed != speed || dln2->mode != mode || 6183d8c0d74SLaurentiu Palcu dln2->bpw != bpw; 6193d8c0d74SLaurentiu Palcu 6203d8c0d74SLaurentiu Palcu if (!bus_setup_change) 6213d8c0d74SLaurentiu Palcu return 0; 6223d8c0d74SLaurentiu Palcu 6233d8c0d74SLaurentiu Palcu ret = dln2_spi_enable(dln2, false); 6243d8c0d74SLaurentiu Palcu if (ret < 0) 6253d8c0d74SLaurentiu Palcu return ret; 6263d8c0d74SLaurentiu Palcu 6273d8c0d74SLaurentiu Palcu if (dln2->speed != speed) { 6283d8c0d74SLaurentiu Palcu ret = dln2_spi_set_speed(dln2, speed); 6293d8c0d74SLaurentiu Palcu if (ret < 0) 6303d8c0d74SLaurentiu Palcu return ret; 6313d8c0d74SLaurentiu Palcu 6323d8c0d74SLaurentiu Palcu dln2->speed = speed; 6333d8c0d74SLaurentiu Palcu } 6343d8c0d74SLaurentiu Palcu 6353d8c0d74SLaurentiu Palcu if (dln2->mode != mode) { 6363d8c0d74SLaurentiu Palcu ret = dln2_spi_set_mode(dln2, mode & 0x3); 6373d8c0d74SLaurentiu Palcu if (ret < 0) 6383d8c0d74SLaurentiu Palcu return ret; 6393d8c0d74SLaurentiu Palcu 6403d8c0d74SLaurentiu Palcu dln2->mode = mode; 6413d8c0d74SLaurentiu Palcu } 6423d8c0d74SLaurentiu Palcu 6433d8c0d74SLaurentiu Palcu if (dln2->bpw != bpw) { 6443d8c0d74SLaurentiu Palcu ret = dln2_spi_set_bpw(dln2, bpw); 6453d8c0d74SLaurentiu Palcu if (ret < 0) 6463d8c0d74SLaurentiu Palcu return ret; 6473d8c0d74SLaurentiu Palcu 6483d8c0d74SLaurentiu Palcu dln2->bpw = bpw; 6493d8c0d74SLaurentiu Palcu } 6503d8c0d74SLaurentiu Palcu 6512b5e368eSLaurentiu Palcu return dln2_spi_enable(dln2, true); 6523d8c0d74SLaurentiu Palcu } 6533d8c0d74SLaurentiu Palcu 6543d8c0d74SLaurentiu Palcu static int dln2_spi_transfer_one(struct spi_master *master, 6553d8c0d74SLaurentiu Palcu struct spi_device *spi, 6563d8c0d74SLaurentiu Palcu struct spi_transfer *xfer) 6573d8c0d74SLaurentiu Palcu { 6583d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 6593d8c0d74SLaurentiu Palcu int status; 6603d8c0d74SLaurentiu Palcu u8 attr = 0; 6613d8c0d74SLaurentiu Palcu 6623d8c0d74SLaurentiu Palcu status = dln2_spi_transfer_setup(dln2, xfer->speed_hz, 6633d8c0d74SLaurentiu Palcu xfer->bits_per_word, 6643d8c0d74SLaurentiu Palcu spi->mode); 6653d8c0d74SLaurentiu Palcu if (status < 0) { 6663d8c0d74SLaurentiu Palcu dev_err(&dln2->pdev->dev, "Cannot setup transfer\n"); 6673d8c0d74SLaurentiu Palcu return status; 6683d8c0d74SLaurentiu Palcu } 6693d8c0d74SLaurentiu Palcu 6703d8c0d74SLaurentiu Palcu if (!xfer->cs_change && !spi_transfer_is_last(master, xfer)) 6713d8c0d74SLaurentiu Palcu attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; 6723d8c0d74SLaurentiu Palcu 6733d8c0d74SLaurentiu Palcu status = dln2_spi_rdwr(dln2, xfer->tx_buf, xfer->rx_buf, 6743d8c0d74SLaurentiu Palcu xfer->len, attr); 6753d8c0d74SLaurentiu Palcu if (status < 0) 6763d8c0d74SLaurentiu Palcu dev_err(&dln2->pdev->dev, "write/read failed!\n"); 6773d8c0d74SLaurentiu Palcu 6783d8c0d74SLaurentiu Palcu return status; 6793d8c0d74SLaurentiu Palcu } 6803d8c0d74SLaurentiu Palcu 6813d8c0d74SLaurentiu Palcu static int dln2_spi_probe(struct platform_device *pdev) 6823d8c0d74SLaurentiu Palcu { 6833d8c0d74SLaurentiu Palcu struct spi_master *master; 6843d8c0d74SLaurentiu Palcu struct dln2_spi *dln2; 6853d8c0d74SLaurentiu Palcu struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); 6863d8c0d74SLaurentiu Palcu int ret; 6873d8c0d74SLaurentiu Palcu 6883d8c0d74SLaurentiu Palcu master = spi_alloc_master(&pdev->dev, sizeof(*dln2)); 6893d8c0d74SLaurentiu Palcu if (!master) 6903d8c0d74SLaurentiu Palcu return -ENOMEM; 6913d8c0d74SLaurentiu Palcu 6923d8c0d74SLaurentiu Palcu platform_set_drvdata(pdev, master); 6933d8c0d74SLaurentiu Palcu 6943d8c0d74SLaurentiu Palcu dln2 = spi_master_get_devdata(master); 6953d8c0d74SLaurentiu Palcu 6963d8c0d74SLaurentiu Palcu dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); 6973d8c0d74SLaurentiu Palcu if (!dln2->buf) { 6983d8c0d74SLaurentiu Palcu ret = -ENOMEM; 6993d8c0d74SLaurentiu Palcu goto exit_free_master; 7003d8c0d74SLaurentiu Palcu } 7013d8c0d74SLaurentiu Palcu 7023d8c0d74SLaurentiu Palcu dln2->master = master; 7033d8c0d74SLaurentiu Palcu dln2->pdev = pdev; 7043d8c0d74SLaurentiu Palcu dln2->port = pdata->port; 7053d8c0d74SLaurentiu Palcu /* cs/mode can never be 0xff, so the first transfer will set them */ 7063d8c0d74SLaurentiu Palcu dln2->cs = 0xff; 7073d8c0d74SLaurentiu Palcu dln2->mode = 0xff; 7083d8c0d74SLaurentiu Palcu 7093d8c0d74SLaurentiu Palcu /* disable SPI module before continuing with the setup */ 7103d8c0d74SLaurentiu Palcu ret = dln2_spi_enable(dln2, false); 7113d8c0d74SLaurentiu Palcu if (ret < 0) { 7123d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to disable SPI module\n"); 7133d8c0d74SLaurentiu Palcu goto exit_free_master; 7143d8c0d74SLaurentiu Palcu } 7153d8c0d74SLaurentiu Palcu 7163d8c0d74SLaurentiu Palcu ret = dln2_spi_get_cs_num(dln2, &master->num_chipselect); 7173d8c0d74SLaurentiu Palcu if (ret < 0) { 7183d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to get number of CS pins\n"); 7193d8c0d74SLaurentiu Palcu goto exit_free_master; 7203d8c0d74SLaurentiu Palcu } 7213d8c0d74SLaurentiu Palcu 7223d8c0d74SLaurentiu Palcu ret = dln2_spi_get_speed_range(dln2, 7233d8c0d74SLaurentiu Palcu &master->min_speed_hz, 7243d8c0d74SLaurentiu Palcu &master->max_speed_hz); 7253d8c0d74SLaurentiu Palcu if (ret < 0) { 7263d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); 7273d8c0d74SLaurentiu Palcu goto exit_free_master; 7283d8c0d74SLaurentiu Palcu } 7293d8c0d74SLaurentiu Palcu 7303d8c0d74SLaurentiu Palcu ret = dln2_spi_get_supported_frame_sizes(dln2, 7313d8c0d74SLaurentiu Palcu &master->bits_per_word_mask); 7323d8c0d74SLaurentiu Palcu if (ret < 0) { 7333d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); 7343d8c0d74SLaurentiu Palcu goto exit_free_master; 7353d8c0d74SLaurentiu Palcu } 7363d8c0d74SLaurentiu Palcu 7373d8c0d74SLaurentiu Palcu ret = dln2_spi_cs_enable_all(dln2, true); 7383d8c0d74SLaurentiu Palcu if (ret < 0) { 7393d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to enable CS pins\n"); 7403d8c0d74SLaurentiu Palcu goto exit_free_master; 7413d8c0d74SLaurentiu Palcu } 7423d8c0d74SLaurentiu Palcu 7433d8c0d74SLaurentiu Palcu master->bus_num = -1; 7443d8c0d74SLaurentiu Palcu master->mode_bits = SPI_CPOL | SPI_CPHA; 7453d8c0d74SLaurentiu Palcu master->prepare_message = dln2_spi_prepare_message; 7463d8c0d74SLaurentiu Palcu master->transfer_one = dln2_spi_transfer_one; 7473d8c0d74SLaurentiu Palcu master->auto_runtime_pm = true; 7483d8c0d74SLaurentiu Palcu 7493d8c0d74SLaurentiu Palcu /* enable SPI module, we're good to go */ 7503d8c0d74SLaurentiu Palcu ret = dln2_spi_enable(dln2, true); 7513d8c0d74SLaurentiu Palcu if (ret < 0) { 7523d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to enable SPI module\n"); 7533d8c0d74SLaurentiu Palcu goto exit_free_master; 7543d8c0d74SLaurentiu Palcu } 7553d8c0d74SLaurentiu Palcu 7563d8c0d74SLaurentiu Palcu pm_runtime_set_autosuspend_delay(&pdev->dev, 7573d8c0d74SLaurentiu Palcu DLN2_RPM_AUTOSUSPEND_TIMEOUT); 7583d8c0d74SLaurentiu Palcu pm_runtime_use_autosuspend(&pdev->dev); 7593d8c0d74SLaurentiu Palcu pm_runtime_set_active(&pdev->dev); 7603d8c0d74SLaurentiu Palcu pm_runtime_enable(&pdev->dev); 7613d8c0d74SLaurentiu Palcu 7623d8c0d74SLaurentiu Palcu ret = devm_spi_register_master(&pdev->dev, master); 7633d8c0d74SLaurentiu Palcu if (ret < 0) { 7643d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to register master\n"); 7653d8c0d74SLaurentiu Palcu goto exit_register; 7663d8c0d74SLaurentiu Palcu } 7673d8c0d74SLaurentiu Palcu 7683d8c0d74SLaurentiu Palcu return ret; 7693d8c0d74SLaurentiu Palcu 7703d8c0d74SLaurentiu Palcu exit_register: 7713d8c0d74SLaurentiu Palcu pm_runtime_disable(&pdev->dev); 7723d8c0d74SLaurentiu Palcu pm_runtime_set_suspended(&pdev->dev); 7733d8c0d74SLaurentiu Palcu 7743d8c0d74SLaurentiu Palcu if (dln2_spi_enable(dln2, false) < 0) 7753d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to disable SPI module\n"); 7763d8c0d74SLaurentiu Palcu exit_free_master: 7773d8c0d74SLaurentiu Palcu spi_master_put(master); 7783d8c0d74SLaurentiu Palcu 7793d8c0d74SLaurentiu Palcu return ret; 7803d8c0d74SLaurentiu Palcu } 7813d8c0d74SLaurentiu Palcu 7823d8c0d74SLaurentiu Palcu static int dln2_spi_remove(struct platform_device *pdev) 7833d8c0d74SLaurentiu Palcu { 7843d8c0d74SLaurentiu Palcu struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); 7853d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 7863d8c0d74SLaurentiu Palcu 7873d8c0d74SLaurentiu Palcu pm_runtime_disable(&pdev->dev); 7883d8c0d74SLaurentiu Palcu 7893d8c0d74SLaurentiu Palcu if (dln2_spi_enable(dln2, false) < 0) 7903d8c0d74SLaurentiu Palcu dev_err(&pdev->dev, "Failed to disable SPI module\n"); 7913d8c0d74SLaurentiu Palcu 7923d8c0d74SLaurentiu Palcu return 0; 7933d8c0d74SLaurentiu Palcu } 7943d8c0d74SLaurentiu Palcu 7953d8c0d74SLaurentiu Palcu #ifdef CONFIG_PM_SLEEP 7963d8c0d74SLaurentiu Palcu static int dln2_spi_suspend(struct device *dev) 7973d8c0d74SLaurentiu Palcu { 7983d8c0d74SLaurentiu Palcu int ret; 7993d8c0d74SLaurentiu Palcu struct spi_master *master = dev_get_drvdata(dev); 8003d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 8013d8c0d74SLaurentiu Palcu 8023d8c0d74SLaurentiu Palcu ret = spi_master_suspend(master); 8033d8c0d74SLaurentiu Palcu if (ret < 0) 8043d8c0d74SLaurentiu Palcu return ret; 8053d8c0d74SLaurentiu Palcu 8063d8c0d74SLaurentiu Palcu if (!pm_runtime_suspended(dev)) { 8073d8c0d74SLaurentiu Palcu ret = dln2_spi_enable(dln2, false); 8083d8c0d74SLaurentiu Palcu if (ret < 0) 8093d8c0d74SLaurentiu Palcu return ret; 8103d8c0d74SLaurentiu Palcu } 8113d8c0d74SLaurentiu Palcu 8123d8c0d74SLaurentiu Palcu /* 8133d8c0d74SLaurentiu Palcu * USB power may be cut off during sleep. Resetting the following 8143d8c0d74SLaurentiu Palcu * parameters will force the board to be set up before first transfer. 8153d8c0d74SLaurentiu Palcu */ 8163d8c0d74SLaurentiu Palcu dln2->cs = 0xff; 8173d8c0d74SLaurentiu Palcu dln2->speed = 0; 8183d8c0d74SLaurentiu Palcu dln2->bpw = 0; 8193d8c0d74SLaurentiu Palcu dln2->mode = 0xff; 8203d8c0d74SLaurentiu Palcu 8213d8c0d74SLaurentiu Palcu return 0; 8223d8c0d74SLaurentiu Palcu } 8233d8c0d74SLaurentiu Palcu 8243d8c0d74SLaurentiu Palcu static int dln2_spi_resume(struct device *dev) 8253d8c0d74SLaurentiu Palcu { 8263d8c0d74SLaurentiu Palcu int ret; 8273d8c0d74SLaurentiu Palcu struct spi_master *master = dev_get_drvdata(dev); 8283d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 8293d8c0d74SLaurentiu Palcu 8303d8c0d74SLaurentiu Palcu if (!pm_runtime_suspended(dev)) { 8313d8c0d74SLaurentiu Palcu ret = dln2_spi_cs_enable_all(dln2, true); 8323d8c0d74SLaurentiu Palcu if (ret < 0) 8333d8c0d74SLaurentiu Palcu return ret; 8343d8c0d74SLaurentiu Palcu 8353d8c0d74SLaurentiu Palcu ret = dln2_spi_enable(dln2, true); 8363d8c0d74SLaurentiu Palcu if (ret < 0) 8373d8c0d74SLaurentiu Palcu return ret; 8383d8c0d74SLaurentiu Palcu } 8393d8c0d74SLaurentiu Palcu 8403d8c0d74SLaurentiu Palcu return spi_master_resume(master); 8413d8c0d74SLaurentiu Palcu } 8423d8c0d74SLaurentiu Palcu #endif /* CONFIG_PM_SLEEP */ 8433d8c0d74SLaurentiu Palcu 844b2e5dda1SMark Brown #ifdef CONFIG_PM 8453d8c0d74SLaurentiu Palcu static int dln2_spi_runtime_suspend(struct device *dev) 8463d8c0d74SLaurentiu Palcu { 8473d8c0d74SLaurentiu Palcu struct spi_master *master = dev_get_drvdata(dev); 8483d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 8493d8c0d74SLaurentiu Palcu 8503d8c0d74SLaurentiu Palcu return dln2_spi_enable(dln2, false); 8513d8c0d74SLaurentiu Palcu } 8523d8c0d74SLaurentiu Palcu 8533d8c0d74SLaurentiu Palcu static int dln2_spi_runtime_resume(struct device *dev) 8543d8c0d74SLaurentiu Palcu { 8553d8c0d74SLaurentiu Palcu struct spi_master *master = dev_get_drvdata(dev); 8563d8c0d74SLaurentiu Palcu struct dln2_spi *dln2 = spi_master_get_devdata(master); 8573d8c0d74SLaurentiu Palcu 8583d8c0d74SLaurentiu Palcu return dln2_spi_enable(dln2, true); 8593d8c0d74SLaurentiu Palcu } 8602b5e368eSLaurentiu Palcu #endif /* CONFIG_PM */ 8613d8c0d74SLaurentiu Palcu 8623d8c0d74SLaurentiu Palcu static const struct dev_pm_ops dln2_spi_pm = { 8633d8c0d74SLaurentiu Palcu SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume) 8643d8c0d74SLaurentiu Palcu SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend, 8653d8c0d74SLaurentiu Palcu dln2_spi_runtime_resume, NULL) 8663d8c0d74SLaurentiu Palcu }; 8673d8c0d74SLaurentiu Palcu 8683d8c0d74SLaurentiu Palcu static struct platform_driver spi_dln2_driver = { 8693d8c0d74SLaurentiu Palcu .driver = { 8703d8c0d74SLaurentiu Palcu .name = "dln2-spi", 8713d8c0d74SLaurentiu Palcu .pm = &dln2_spi_pm, 8723d8c0d74SLaurentiu Palcu }, 8733d8c0d74SLaurentiu Palcu .probe = dln2_spi_probe, 8743d8c0d74SLaurentiu Palcu .remove = dln2_spi_remove, 8753d8c0d74SLaurentiu Palcu }; 8763d8c0d74SLaurentiu Palcu module_platform_driver(spi_dln2_driver); 8773d8c0d74SLaurentiu Palcu 8783d8c0d74SLaurentiu Palcu MODULE_DESCRIPTION("Driver for the Diolan DLN2 SPI master interface"); 8793d8c0d74SLaurentiu Palcu MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 8803d8c0d74SLaurentiu Palcu MODULE_LICENSE("GPL v2"); 8813d8c0d74SLaurentiu Palcu MODULE_ALIAS("platform:dln2-spi"); 882