2019-05-20 20:08:10 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-03-14 14:04:51 +03:00
/*
* DaVinci DM816 AHCI SATA platform driver
*
* Copyright ( C ) 2017 BayLibre SAS
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/pm.h>
# include <linux/platform_device.h>
# include <linux/libata.h>
# include <linux/ahci_platform.h>
# include "ahci.h"
# define AHCI_DM816_DRV_NAME "ahci-dm816"
# define AHCI_DM816_PHY_ENPLL(x) ((x) << 0)
# define AHCI_DM816_PHY_MPY(x) ((x) << 1)
# define AHCI_DM816_PHY_LOS(x) ((x) << 12)
# define AHCI_DM816_PHY_RXCDR(x) ((x) << 13)
# define AHCI_DM816_PHY_RXEQ(x) ((x) << 16)
# define AHCI_DM816_PHY_TXSWING(x) ((x) << 23)
# define AHCI_DM816_P0PHYCR_REG 0x178
# define AHCI_DM816_P1PHYCR_REG 0x1f8
# define AHCI_DM816_PLL_OUT 1500000000LU
static const unsigned long pll_mpy_table [ ] = {
400 , 500 , 600 , 800 , 825 , 1000 , 1200 ,
1250 , 1500 , 1600 , 1650 , 2000 , 2200 , 2500
} ;
static int ahci_dm816_get_mpy_bits ( unsigned long refclk_rate )
{
unsigned long pll_multiplier ;
int i ;
/*
* We need to determine the value of the multiplier ( MPY ) bits .
* In order to include the 8.25 multiplier we need to first divide
* the refclk rate by 100.
*/
pll_multiplier = AHCI_DM816_PLL_OUT / ( refclk_rate / 100 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pll_mpy_table ) ; i + + ) {
if ( pll_mpy_table [ i ] = = pll_multiplier )
return i ;
}
/*
* We should have divided evenly - if not , return an invalid
* value .
*/
return - 1 ;
}
static int ahci_dm816_phy_init ( struct ahci_host_priv * hpriv , struct device * dev )
{
unsigned long refclk_rate ;
int mpy ;
u32 val ;
/*
* We should have been supplied two clocks : the functional and
* keep - alive clock and the external reference clock . We need the
* rate of the latter to calculate the correct value of MPY bits .
*/
2022-09-09 22:36:05 +03:00
if ( hpriv - > n_clks < 2 ) {
2017-03-14 14:04:51 +03:00
dev_err ( dev , " reference clock not supplied \n " ) ;
return - EINVAL ;
}
2022-09-09 22:36:05 +03:00
refclk_rate = clk_get_rate ( hpriv - > clks [ 1 ] . clk ) ;
2017-03-14 14:04:51 +03:00
if ( ( refclk_rate % 100 ) ! = 0 ) {
dev_err ( dev , " reference clock rate must be divisible by 100 \n " ) ;
return - EINVAL ;
}
mpy = ahci_dm816_get_mpy_bits ( refclk_rate ) ;
if ( mpy < 0 ) {
dev_err ( dev , " can't calculate the MPY bits value \n " ) ;
return - EINVAL ;
}
/* Enable the PHY and configure the first HBA port. */
val = AHCI_DM816_PHY_MPY ( mpy ) | AHCI_DM816_PHY_LOS ( 1 ) |
AHCI_DM816_PHY_RXCDR ( 4 ) | AHCI_DM816_PHY_RXEQ ( 1 ) |
AHCI_DM816_PHY_TXSWING ( 3 ) | AHCI_DM816_PHY_ENPLL ( 1 ) ;
writel ( val , hpriv - > mmio + AHCI_DM816_P0PHYCR_REG ) ;
/* Configure the second HBA port. */
val = AHCI_DM816_PHY_LOS ( 1 ) | AHCI_DM816_PHY_RXCDR ( 4 ) |
AHCI_DM816_PHY_RXEQ ( 1 ) | AHCI_DM816_PHY_TXSWING ( 3 ) ;
writel ( val , hpriv - > mmio + AHCI_DM816_P1PHYCR_REG ) ;
return 0 ;
}
static int ahci_dm816_softreset ( struct ata_link * link ,
unsigned int * class , unsigned long deadline )
{
int pmp , ret ;
pmp = sata_srst_pmp ( link ) ;
/*
* There ' s an issue with the SATA controller on DM816 SoC : if we
* enable Port Multiplier support , but the drive is connected directly
* to the board , it can ' t be detected . As a workaround : if PMP is
* enabled , we first call ahci_do_softreset ( ) and pass it the result of
* sata_srst_pmp ( ) . If this call fails , we retry with pmp = 0.
*/
ret = ahci_do_softreset ( link , class , pmp , deadline , ahci_check_ready ) ;
if ( pmp & & ret = = - EBUSY )
return ahci_do_softreset ( link , class , 0 ,
deadline , ahci_check_ready ) ;
return ret ;
}
static struct ata_port_operations ahci_dm816_port_ops = {
. inherits = & ahci_platform_ops ,
. softreset = ahci_dm816_softreset ,
} ;
static const struct ata_port_info ahci_dm816_port_info = {
. flags = AHCI_FLAG_COMMON ,
. pio_mask = ATA_PIO4 ,
. udma_mask = ATA_UDMA6 ,
. port_ops = & ahci_dm816_port_ops ,
} ;
2023-03-22 22:53:59 +03:00
static const struct scsi_host_template ahci_dm816_platform_sht = {
2017-03-14 14:04:51 +03:00
AHCI_SHT ( AHCI_DM816_DRV_NAME ) ,
} ;
static int ahci_dm816_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct ahci_host_priv * hpriv ;
int rc ;
2018-08-22 15:13:01 +03:00
hpriv = ahci_platform_get_resources ( pdev , 0 ) ;
2017-03-14 14:04:51 +03:00
if ( IS_ERR ( hpriv ) )
return PTR_ERR ( hpriv ) ;
rc = ahci_platform_enable_resources ( hpriv ) ;
if ( rc )
return rc ;
rc = ahci_dm816_phy_init ( hpriv , dev ) ;
if ( rc )
goto disable_resources ;
rc = ahci_platform_init_host ( pdev , hpriv ,
& ahci_dm816_port_info ,
& ahci_dm816_platform_sht ) ;
if ( rc )
goto disable_resources ;
return 0 ;
disable_resources :
ahci_platform_disable_resources ( hpriv ) ;
return rc ;
}
static SIMPLE_DEV_PM_OPS ( ahci_dm816_pm_ops ,
ahci_platform_suspend ,
ahci_platform_resume ) ;
static const struct of_device_id ahci_dm816_of_match [ ] = {
{ . compatible = " ti,dm816-ahci " , } ,
2022-03-03 11:36:35 +03:00
{ /* sentinel */ }
2017-03-14 14:04:51 +03:00
} ;
MODULE_DEVICE_TABLE ( of , ahci_dm816_of_match ) ;
static struct platform_driver ahci_dm816_driver = {
. probe = ahci_dm816_probe ,
2023-05-12 23:46:46 +03:00
. remove_new = ata_platform_remove_one ,
2017-03-14 14:04:51 +03:00
. driver = {
. name = AHCI_DM816_DRV_NAME ,
. of_match_table = ahci_dm816_of_match ,
. pm = & ahci_dm816_pm_ops ,
} ,
} ;
module_platform_driver ( ahci_dm816_driver ) ;
MODULE_DESCRIPTION ( " DaVinci DM816 AHCI SATA platform driver " ) ;
MODULE_AUTHOR ( " Bartosz Golaszewski <bgolaszewski@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;