From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- drivers/staging/rts5208/spi.c | 906 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 906 insertions(+) create mode 100644 drivers/staging/rts5208/spi.c (limited to 'drivers/staging/rts5208/spi.c') diff --git a/drivers/staging/rts5208/spi.c b/drivers/staging/rts5208/spi.c new file mode 100644 index 000000000..e88fe1a99 --- /dev/null +++ b/drivers/staging/rts5208/spi.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Wei WANG (wei_wang@realsil.com.cn) + * Micky Ching (micky_ching@realsil.com.cn) + */ + +#include +#include +#include + +#include "rtsx.h" +#include "spi.h" + +static inline void spi_set_err_code(struct rtsx_chip *chip, u8 err_code) +{ + struct spi_info *spi = &chip->spi; + + spi->err_code = err_code; +} + +static int spi_init(struct rtsx_chip *chip) +{ + int retval; + + retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF, + CS_POLARITY_LOW | DTO_MSB_FIRST + | SPI_MASTER | SPI_MODE0 | SPI_AUTO); + if (retval) + return retval; + retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK, + SAMPLE_DELAY_HALF); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +static int spi_set_init_para(struct rtsx_chip *chip) +{ + struct spi_info *spi = &chip->spi; + int retval; + + retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF, + (u8)(spi->clk_div >> 8)); + if (retval) + return retval; + retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF, + (u8)(spi->clk_div)); + if (retval) + return retval; + + retval = switch_clock(chip, spi->spi_clock); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = select_card(chip, SPI_CARD); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN, + SPI_CLK_EN); + if (retval) + return retval; + retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN, + SPI_OUTPUT_EN); + if (retval) + return retval; + + wait_timeout(10); + + retval = spi_init(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + return STATUS_SUCCESS; +} + +static int sf_polling_status(struct rtsx_chip *chip, int msec) +{ + int retval; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, SPI_RDSR); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_POLLING_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, msec); + if (retval < 0) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_BUSY_ERR); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +static int sf_enable_write(struct rtsx_chip *chip, u8 ins) +{ + struct spi_info *spi = &chip->spi; + int retval; + + if (!spi->write_en) + return STATUS_SUCCESS; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_C_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +static int sf_disable_write(struct rtsx_chip *chip, u8 ins) +{ + struct spi_info *spi = &chip->spi; + int retval; + + if (!spi->write_en) + return STATUS_SUCCESS; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_C_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +static void sf_program(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr, + u16 len) +{ + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, (u8)len); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, (u8)(len >> 8)); + if (addr_mode) { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, + (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, + (u8)(addr >> 16)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CADO_MODE0); + } else { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CDO_MODE0); + } + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); +} + +static int sf_erase(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr) +{ + int retval; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + if (addr_mode) { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, + (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, + (u8)(addr >> 16)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CA_MODE0); + } else { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_C_MODE0); + } + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +static int spi_init_eeprom(struct rtsx_chip *chip) +{ + int retval; + int clk; + + if (chip->asic_code) + clk = 30; + else + clk = CLK_30; + + retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF, 0x00); + if (retval) + return retval; + retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF, 0x27); + if (retval) + return retval; + + retval = switch_clock(chip, clk); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = select_card(chip, SPI_CARD); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN, + SPI_CLK_EN); + if (retval) + return retval; + retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN, + SPI_OUTPUT_EN); + if (retval) + return retval; + + wait_timeout(10); + + retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF, + CS_POLARITY_HIGH | SPI_EEPROM_AUTO); + if (retval) + return retval; + retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK, + SAMPLE_DELAY_HALF); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +static int spi_eeprom_program_enable(struct rtsx_chip *chip) +{ + int retval; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x86); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x13); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CA_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) + return STATUS_FAIL; + + return STATUS_SUCCESS; +} + +int spi_erase_eeprom_chip(struct rtsx_chip *chip) +{ + int retval; + + retval = spi_init_eeprom(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = spi_eeprom_program_enable(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x12); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x84); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CA_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) + return STATUS_FAIL; + + retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr) +{ + int retval; + + retval = spi_init_eeprom(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = spi_eeprom_program_enable(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x07); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CA_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) + return STATUS_FAIL; + + retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val) +{ + int retval; + u8 data; + + retval = spi_init_eeprom(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x06); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CADI_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) + return STATUS_FAIL; + + wait_timeout(5); + retval = rtsx_read_register(chip, SPI_DATA, &data); + if (retval) + return retval; + + if (val) + *val = data; + + retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val) +{ + int retval; + + retval = spi_init_eeprom(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = spi_eeprom_program_enable(chip); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x05); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, val); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x4E); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CA_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) + return STATUS_FAIL; + + retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); + if (retval) + return retval; + + return STATUS_SUCCESS; +} + +int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + struct spi_info *spi = &chip->spi; + + dev_dbg(rtsx_dev(chip), "%s: err_code = 0x%x\n", __func__, + spi->err_code); + rtsx_stor_set_xfer_buf(&spi->err_code, + min_t(int, scsi_bufflen(srb), 1), srb); + scsi_set_resid(srb, scsi_bufflen(srb) - 1); + + return STATUS_SUCCESS; +} + +int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + struct spi_info *spi = &chip->spi; + + spi_set_err_code(chip, SPI_NO_ERR); + + if (chip->asic_code) + spi->spi_clock = ((u16)(srb->cmnd[8]) << 8) | srb->cmnd[9]; + else + spi->spi_clock = srb->cmnd[3]; + + spi->clk_div = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; + spi->write_en = srb->cmnd[6]; + + dev_dbg(rtsx_dev(chip), "spi_clock = %d, clk_div = %d, write_en = %d\n", + spi->spi_clock, spi->clk_div, spi->write_en); + + return STATUS_SUCCESS; +} + +int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + int retval; + u16 len; + u8 *buf; + + spi_set_err_code(chip, SPI_NO_ERR); + + len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; + if (len > 512) { + spi_set_err_code(chip, SPI_INVALID_COMMAND); + return STATUS_FAIL; + } + + retval = spi_set_init_para(chip); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, + PINGPONG_BUFFER); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, srb->cmnd[3]); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, srb->cmnd[4]); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, srb->cmnd[5]); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, srb->cmnd[6]); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, srb->cmnd[7]); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, srb->cmnd[8]); + + if (len == 0) { + if (srb->cmnd[9]) { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, + 0xFF, SPI_TRANSFER0_START | SPI_CA_MODE0); + } else { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, + 0xFF, SPI_TRANSFER0_START | SPI_C_MODE0); + } + } else { + if (srb->cmnd[9]) { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CADI_MODE0); + } else { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CDI_MODE0); + } + } + + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + if (len) { + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return STATUS_ERROR; + + retval = rtsx_read_ppbuf(chip, buf, len); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_READ_ERR); + kfree(buf); + return STATUS_FAIL; + } + + rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb); + scsi_set_resid(srb, 0); + + kfree(buf); + } + + return STATUS_SUCCESS; +} + +int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + int retval; + unsigned int index = 0, offset = 0; + u8 ins, slow_read; + u32 addr; + u16 len; + u8 *buf; + + spi_set_err_code(chip, SPI_NO_ERR); + + ins = srb->cmnd[3]; + addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) + << 8) | srb->cmnd[6]; + len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; + slow_read = srb->cmnd[9]; + + retval = spi_set_init_para(chip); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL); + if (!buf) + return STATUS_ERROR; + + while (len) { + u16 pagelen = SF_PAGE_LEN - (u8)addr; + + if (pagelen > len) + pagelen = len; + + rtsx_init_cmd(chip); + + trans_dma_enable(DMA_FROM_DEVICE, chip, 256, DMA_256); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + + if (slow_read) { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, + (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, + (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, + (u8)(addr >> 16)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + } else { + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, + (u8)addr); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, + (u8)(addr >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR3, 0xFF, + (u8)(addr >> 16)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_32); + } + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, + (u8)(pagelen >> 8)); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, + (u8)pagelen); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CADI_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, + SPI_TRANSFER0_END, SPI_TRANSFER0_END); + + rtsx_send_cmd_no_wait(chip); + + retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0, + DMA_FROM_DEVICE, 10000); + if (retval < 0) { + kfree(buf); + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, &offset, + TO_XFER_BUF); + + addr += pagelen; + len -= pagelen; + } + + scsi_set_resid(srb, 0); + kfree(buf); + + return STATUS_SUCCESS; +} + +int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + int retval; + u8 ins, program_mode; + u32 addr; + u16 len; + u8 *buf; + unsigned int index = 0, offset = 0; + + spi_set_err_code(chip, SPI_NO_ERR); + + ins = srb->cmnd[3]; + addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) + << 8) | srb->cmnd[6]; + len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; + program_mode = srb->cmnd[9]; + + retval = spi_set_init_para(chip); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + if (program_mode == BYTE_PROGRAM) { + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return STATUS_ERROR; + + while (len) { + retval = sf_enable_write(chip, SPI_WREN); + if (retval != STATUS_SUCCESS) { + kfree(buf); + return STATUS_FAIL; + } + + rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset, + FROM_XFER_BUF); + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, + buf[0]); + sf_program(chip, ins, 1, addr, 1); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + kfree(buf); + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + retval = sf_polling_status(chip, 100); + if (retval != STATUS_SUCCESS) { + kfree(buf); + return STATUS_FAIL; + } + + addr++; + len--; + } + + kfree(buf); + + } else if (program_mode == AAI_PROGRAM) { + int first_byte = 1; + + retval = sf_enable_write(chip, SPI_WREN); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return STATUS_ERROR; + + while (len) { + rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset, + FROM_XFER_BUF); + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, + buf[0]); + if (first_byte) { + sf_program(chip, ins, 1, addr, 1); + first_byte = 0; + } else { + sf_program(chip, ins, 0, 0, 1); + } + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval < 0) { + kfree(buf); + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + retval = sf_polling_status(chip, 100); + if (retval != STATUS_SUCCESS) { + kfree(buf); + return STATUS_FAIL; + } + + len--; + } + + kfree(buf); + + retval = sf_disable_write(chip, SPI_WRDI); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = sf_polling_status(chip, 100); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + } else if (program_mode == PAGE_PROGRAM) { + buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL); + if (!buf) + return STATUS_NOMEM; + + while (len) { + u16 pagelen = SF_PAGE_LEN - (u8)addr; + + if (pagelen > len) + pagelen = len; + + retval = sf_enable_write(chip, SPI_WREN); + if (retval != STATUS_SUCCESS) { + kfree(buf); + return STATUS_FAIL; + } + + rtsx_init_cmd(chip); + + trans_dma_enable(DMA_TO_DEVICE, chip, 256, DMA_256); + sf_program(chip, ins, 1, addr, pagelen); + + rtsx_send_cmd_no_wait(chip); + + rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, + &offset, FROM_XFER_BUF); + + retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0, + DMA_TO_DEVICE, 100); + if (retval < 0) { + kfree(buf); + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + retval = sf_polling_status(chip, 100); + if (retval != STATUS_SUCCESS) { + kfree(buf); + return STATUS_FAIL; + } + + addr += pagelen; + len -= pagelen; + } + + kfree(buf); + } else { + spi_set_err_code(chip, SPI_INVALID_COMMAND); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + int retval; + u8 ins, erase_mode; + u32 addr; + + spi_set_err_code(chip, SPI_NO_ERR); + + ins = srb->cmnd[3]; + addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) + << 8) | srb->cmnd[6]; + erase_mode = srb->cmnd[9]; + + retval = spi_set_init_para(chip); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + if (erase_mode == PAGE_ERASE) { + retval = sf_enable_write(chip, SPI_WREN); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = sf_erase(chip, ins, 1, addr); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + } else if (erase_mode == CHIP_ERASE) { + retval = sf_enable_write(chip, SPI_WREN); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + retval = sf_erase(chip, ins, 0, 0); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + } else { + spi_set_err_code(chip, SPI_INVALID_COMMAND); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} + +int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) +{ + int retval; + u8 ins, status, ewsr; + + ins = srb->cmnd[3]; + status = srb->cmnd[4]; + ewsr = srb->cmnd[5]; + + retval = spi_set_init_para(chip); + if (retval != STATUS_SUCCESS) { + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + retval = sf_enable_write(chip, ewsr); + if (retval != STATUS_SUCCESS) + return STATUS_FAIL; + + rtsx_init_cmd(chip); + + rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, + PINGPONG_BUFFER); + + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, + SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, 0); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1); + rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, status); + rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, + SPI_TRANSFER0_START | SPI_CDO_MODE0); + rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, + SPI_TRANSFER0_END); + + retval = rtsx_send_cmd(chip, 0, 100); + if (retval != STATUS_SUCCESS) { + rtsx_clear_spi_error(chip); + spi_set_err_code(chip, SPI_HW_ERR); + return STATUS_FAIL; + } + + return STATUS_SUCCESS; +} -- cgit v1.2.3