2020-02-10 06:41:51 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs
//
// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com>
//
// Based on spi-mt7621.c:
// Copyright (C) 2011 Sergiy <piratfm@gmail.com>
// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/spi/spi.h>
# define DRIVER_NAME "spi-ar934x"
# define AR934X_SPI_REG_FS 0x00
# define AR934X_SPI_ENABLE BIT(0)
# define AR934X_SPI_REG_IOC 0x08
# define AR934X_SPI_IOC_INITVAL 0x70000
# define AR934X_SPI_REG_CTRL 0x04
# define AR934X_SPI_CLK_MASK GENMASK(5, 0)
# define AR934X_SPI_DATAOUT 0x10
# define AR934X_SPI_REG_SHIFT_CTRL 0x14
# define AR934X_SPI_SHIFT_EN BIT(31)
# define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n))
# define AR934X_SPI_SHIFT_TERM 26
# define AR934X_SPI_SHIFT_VAL(cs, term, count) \
( AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS ( cs ) | \
( term ) < < AR934X_SPI_SHIFT_TERM | ( count ) )
# define AR934X_SPI_DATAIN 0x18
struct ar934x_spi {
struct spi_controller * ctlr ;
void __iomem * base ;
struct clk * clk ;
unsigned int clk_freq ;
} ;
static inline int ar934x_spi_clk_div ( struct ar934x_spi * sp , unsigned int freq )
{
int div = DIV_ROUND_UP ( sp - > clk_freq , freq * 2 ) - 1 ;
if ( div < 0 )
return 0 ;
else if ( div > AR934X_SPI_CLK_MASK )
return - EINVAL ;
else
return div ;
}
static int ar934x_spi_setup ( struct spi_device * spi )
{
struct ar934x_spi * sp = spi_controller_get_devdata ( spi - > master ) ;
if ( ( spi - > max_speed_hz = = 0 ) | |
( spi - > max_speed_hz > ( sp - > clk_freq / 2 ) ) ) {
spi - > max_speed_hz = sp - > clk_freq / 2 ;
} else if ( spi - > max_speed_hz < ( sp - > clk_freq / 128 ) ) {
dev_err ( & spi - > dev , " spi clock is too low \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int ar934x_spi_transfer_one_message ( struct spi_controller * master ,
struct spi_message * m )
{
struct ar934x_spi * sp = spi_controller_get_devdata ( master ) ;
struct spi_transfer * t = NULL ;
struct spi_device * spi = m - > spi ;
unsigned long trx_done , trx_cur ;
int stat = 0 ;
2021-12-22 08:59:57 +03:00
u8 bpw , term = 0 ;
2020-02-10 06:41:51 +03:00
int div , i ;
u32 reg ;
const u8 * tx_buf ;
u8 * buf ;
m - > actual_length = 0 ;
list_for_each_entry ( t , & m - > transfers , transfer_list ) {
2021-12-22 08:59:57 +03:00
if ( t - > bits_per_word > = 8 & & t - > bits_per_word < 32 )
bpw = t - > bits_per_word > > 3 ;
else
bpw = 4 ;
2020-02-10 06:41:51 +03:00
if ( t - > speed_hz )
div = ar934x_spi_clk_div ( sp , t - > speed_hz ) ;
else
div = ar934x_spi_clk_div ( sp , spi - > max_speed_hz ) ;
if ( div < 0 ) {
stat = - EIO ;
goto msg_done ;
}
reg = ioread32 ( sp - > base + AR934X_SPI_REG_CTRL ) ;
reg & = ~ AR934X_SPI_CLK_MASK ;
reg | = div ;
iowrite32 ( reg , sp - > base + AR934X_SPI_REG_CTRL ) ;
iowrite32 ( 0 , sp - > base + AR934X_SPI_DATAOUT ) ;
2021-12-22 08:59:57 +03:00
for ( trx_done = 0 ; trx_done < t - > len ; trx_done + = bpw ) {
2020-02-10 06:41:51 +03:00
trx_cur = t - > len - trx_done ;
2021-12-22 08:59:57 +03:00
if ( trx_cur > bpw )
trx_cur = bpw ;
2020-02-10 06:41:51 +03:00
else if ( list_is_last ( & t - > transfer_list , & m - > transfers ) )
term = 1 ;
if ( t - > tx_buf ) {
tx_buf = t - > tx_buf + trx_done ;
reg = tx_buf [ 0 ] ;
for ( i = 1 ; i < trx_cur ; i + + )
reg = reg < < 8 | tx_buf [ i ] ;
iowrite32 ( reg , sp - > base + AR934X_SPI_DATAOUT ) ;
}
reg = AR934X_SPI_SHIFT_VAL ( spi - > chip_select , term ,
trx_cur * 8 ) ;
iowrite32 ( reg , sp - > base + AR934X_SPI_REG_SHIFT_CTRL ) ;
stat = readl_poll_timeout (
sp - > base + AR934X_SPI_REG_SHIFT_CTRL , reg ,
! ( reg & AR934X_SPI_SHIFT_EN ) , 0 , 5 ) ;
if ( stat < 0 )
goto msg_done ;
if ( t - > rx_buf ) {
reg = ioread32 ( sp - > base + AR934X_SPI_DATAIN ) ;
buf = t - > rx_buf + trx_done ;
for ( i = 0 ; i < trx_cur ; i + + ) {
buf [ trx_cur - i - 1 ] = reg & 0xff ;
reg > > = 8 ;
}
}
2021-12-22 08:59:58 +03:00
spi_delay_exec ( & t - > word_delay , t ) ;
2020-02-10 06:41:51 +03:00
}
m - > actual_length + = t - > len ;
2021-12-22 08:59:58 +03:00
spi_transfer_delay_exec ( t ) ;
2020-02-10 06:41:51 +03:00
}
msg_done :
m - > status = stat ;
spi_finalize_current_message ( master ) ;
return 0 ;
}
static const struct of_device_id ar934x_spi_match [ ] = {
{ . compatible = " qca,ar934x-spi " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ar934x_spi_match ) ;
static int ar934x_spi_probe ( struct platform_device * pdev )
{
struct spi_controller * ctlr ;
struct ar934x_spi * sp ;
void __iomem * base ;
struct clk * clk ;
int ret ;
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
return PTR_ERR ( clk ) ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2020-12-07 11:17:15 +03:00
ctlr = devm_spi_alloc_master ( & pdev - > dev , sizeof ( * sp ) ) ;
2020-02-10 06:41:51 +03:00
if ( ! ctlr ) {
dev_info ( & pdev - > dev , " failed to allocate spi controller \n " ) ;
2020-12-07 11:17:15 +03:00
ret = - ENOMEM ;
goto err_clk_disable ;
2020-02-10 06:41:51 +03:00
}
/* disable flash mapping and expose spi controller registers */
iowrite32 ( AR934X_SPI_ENABLE , base + AR934X_SPI_REG_FS ) ;
/* restore pins to default state: CSn=1 DO=CLK=0 */
iowrite32 ( AR934X_SPI_IOC_INITVAL , base + AR934X_SPI_REG_IOC ) ;
ctlr - > mode_bits = SPI_LSB_FIRST ;
ctlr - > setup = ar934x_spi_setup ;
ctlr - > transfer_one_message = ar934x_spi_transfer_one_message ;
2021-12-22 08:59:57 +03:00
ctlr - > bits_per_word_mask = SPI_BPW_MASK ( 32 ) | SPI_BPW_MASK ( 24 ) |
SPI_BPW_MASK ( 16 ) | SPI_BPW_MASK ( 8 ) ;
2020-02-10 06:41:51 +03:00
ctlr - > dev . of_node = pdev - > dev . of_node ;
ctlr - > num_chipselect = 3 ;
dev_set_drvdata ( & pdev - > dev , ctlr ) ;
sp = spi_controller_get_devdata ( ctlr ) ;
sp - > base = base ;
sp - > clk = clk ;
sp - > clk_freq = clk_get_rate ( clk ) ;
sp - > ctlr = ctlr ;
2020-12-07 11:17:15 +03:00
ret = spi_register_controller ( ctlr ) ;
if ( ! ret )
return 0 ;
err_clk_disable :
clk_disable_unprepare ( clk ) ;
return ret ;
2020-02-10 06:41:51 +03:00
}
static int ar934x_spi_remove ( struct platform_device * pdev )
{
struct spi_controller * ctlr ;
struct ar934x_spi * sp ;
ctlr = dev_get_drvdata ( & pdev - > dev ) ;
sp = spi_controller_get_devdata ( ctlr ) ;
2020-12-07 11:17:15 +03:00
spi_unregister_controller ( ctlr ) ;
2020-02-10 06:41:51 +03:00
clk_disable_unprepare ( sp - > clk ) ;
return 0 ;
}
static struct platform_driver ar934x_spi_driver = {
. driver = {
. name = DRIVER_NAME ,
. of_match_table = ar934x_spi_match ,
} ,
. probe = ar934x_spi_probe ,
. remove = ar934x_spi_remove ,
} ;
module_platform_driver ( ar934x_spi_driver ) ;
MODULE_DESCRIPTION ( " SPI controller driver for Qualcomm Atheros AR934x/QCA95xx " ) ;
MODULE_AUTHOR ( " Chuanhong Guo <gch981213@gmail.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;