2015-05-13 03:19:47 +02:00
|
|
|
/*
|
|
|
|
* This file is part of the coreboot project.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Google Inc.
|
2015-05-13 03:23:27 +02:00
|
|
|
* Copyright (C) 2015 Intel Corporation.
|
2015-05-13 03:19:47 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <arch/io.h>
|
|
|
|
#include <device/device.h>
|
|
|
|
#include <device/pci.h>
|
2015-05-13 03:23:27 +02:00
|
|
|
#include <gpio.h>
|
|
|
|
#include <soc/pcr.h>
|
2015-05-13 03:19:47 +02:00
|
|
|
#include <soc/iomap.h>
|
|
|
|
#include <soc/pm.h>
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
/* Keep the ordering intact GPP_A ~ G, GPD.
|
|
|
|
* As the gpio/smi functions get_smi_status() and
|
|
|
|
* enable_gpio_groupsmi() depends on this ordering.
|
|
|
|
*/
|
|
|
|
static const GPIO_GROUP_INFO gpio_group_info[] = {
|
|
|
|
/* GPP_A */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM0,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_A_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_A_PAD_MAX,
|
|
|
|
.smistsoffset = R_PCH_PCR_GPIO_GPP_A_SMI_STS,
|
|
|
|
.smienoffset = R_PCH_PCR_GPIO_GPP_A_SMI_EN,
|
|
|
|
},
|
|
|
|
/* GPP_B */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM0,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_B_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_B_PAD_MAX,
|
|
|
|
.smistsoffset = R_PCH_PCR_GPIO_GPP_B_SMI_STS,
|
|
|
|
.smienoffset = R_PCH_PCR_GPIO_GPP_B_SMI_EN,
|
|
|
|
},
|
|
|
|
/* GPP_C */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM1,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_C_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_C_PAD_MAX,
|
|
|
|
.smistsoffset = R_PCH_PCR_GPIO_GPP_C_SMI_STS,
|
|
|
|
.smienoffset = R_PCH_PCR_GPIO_GPP_C_SMI_EN,
|
|
|
|
},
|
|
|
|
/* GPP_D */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM1,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_D_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_D_PAD_MAX,
|
|
|
|
.smistsoffset = R_PCH_PCR_GPIO_GPP_D_SMI_STS,
|
|
|
|
.smienoffset = R_PCH_PCR_GPIO_GPP_D_SMI_EN,
|
|
|
|
},
|
|
|
|
/* GPP_E */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM1,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_E_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_E_PAD_MAX,
|
|
|
|
.smistsoffset = R_PCH_PCR_GPIO_GPP_E_SMI_STS,
|
|
|
|
.smienoffset = R_PCH_PCR_GPIO_GPP_E_SMI_EN,
|
|
|
|
},
|
|
|
|
/* GPP_F */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM3,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_F_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_F_PAD_MAX,
|
|
|
|
.smistsoffset = NO_REGISTER_PROPERTY,
|
|
|
|
.smienoffset = NO_REGISTER_PROPERTY,
|
|
|
|
},
|
|
|
|
/* GPP_G */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM3,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPP_G_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPP_G_PAD_MAX,
|
|
|
|
.smistsoffset = NO_REGISTER_PROPERTY,
|
|
|
|
.smienoffset = NO_REGISTER_PROPERTY,
|
|
|
|
},
|
|
|
|
/* GPP_H */
|
|
|
|
{
|
|
|
|
.community = PID_GPIOCOM2,
|
|
|
|
.padcfgoffset = R_PCH_PCR_GPIO_GPD_PADCFG_OFFSET,
|
|
|
|
.padpergroup = V_PCH_GPIO_GPD_PAD_MAX,
|
|
|
|
.smistsoffset = NO_REGISTER_PROPERTY,
|
|
|
|
.smienoffset = NO_REGISTER_PROPERTY,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2015-05-13 03:19:47 +02:00
|
|
|
/*
|
2015-05-13 03:23:27 +02:00
|
|
|
* SPT has 7 GPIO communities named as GPP_A to GPP_G.
|
|
|
|
* Each community has 24 GPIO PIN.
|
|
|
|
* Below formula to calculate GPIO Pin from GPIO PAD.
|
|
|
|
* PIN# = GROUP_PAD# + GROUP# * 24
|
|
|
|
* ====================================
|
|
|
|
* Community || Group#
|
|
|
|
* ====================================
|
|
|
|
* GPP_A || 0
|
|
|
|
* GPP_B || 1
|
|
|
|
* GPP_C || 2
|
|
|
|
* GPP_D || 3
|
|
|
|
* GPP_E || 4
|
|
|
|
* GPP_F || 5
|
|
|
|
* GPP_G || 6
|
2015-05-13 03:19:47 +02:00
|
|
|
*/
|
2015-05-13 03:23:27 +02:00
|
|
|
static u32 get_padnumber_from_gpiopad(GPIO_PAD gpiopad)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
return (u32) GPIO_GET_PAD_NUMBER(gpiopad);
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
static u32 get_groupindex_from_gpiopad(GPIO_PAD gpiopad)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
return (u32) GPIO_GET_GROUP_INDEX_FROM_PAD(gpiopad);
|
|
|
|
}
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
static int read_write_gpio_pad_reg(u32 gpiopad, u8 dwreg, u32 mask, int write,
|
|
|
|
u32 *readwriteval)
|
|
|
|
{
|
|
|
|
u32 padcfgreg;
|
|
|
|
u32 gpiogroupinfolength;
|
|
|
|
u32 groupindex;
|
|
|
|
u32 padnumber;
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
groupindex = get_groupindex_from_gpiopad(gpiopad);
|
|
|
|
padnumber = get_padnumber_from_gpiopad(gpiopad);
|
|
|
|
|
|
|
|
gpiogroupinfolength = sizeof(gpio_group_info) / sizeof(GPIO_GROUP_INFO);
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
/* Check if group argument exceeds GPIO GROUP INFO array */
|
|
|
|
if ((u32) groupindex >= gpiogroupinfolength)
|
|
|
|
return -1;
|
|
|
|
/* Check if legal pin number */
|
|
|
|
if (padnumber >= gpio_group_info[groupindex].padpergroup)
|
|
|
|
return -1;
|
|
|
|
/* Create Pad Configuration register offset */
|
|
|
|
padcfgreg = 0x8 * padnumber + gpio_group_info[groupindex].padcfgoffset;
|
|
|
|
if (dwreg == 1)
|
|
|
|
padcfgreg += 0x4;
|
|
|
|
if (write) {
|
|
|
|
pcr_andthenor32(gpio_group_info[groupindex].community,
|
|
|
|
padcfgreg, (u32) (~mask),
|
|
|
|
(u32) (*readwriteval & mask));
|
|
|
|
} else {
|
|
|
|
pcr_read32(gpio_group_info[groupindex].community, padcfgreg,
|
|
|
|
readwriteval);
|
|
|
|
*readwriteval &= mask;
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
2015-05-13 03:23:27 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
static int convert_gpio_num_to_pad(gpio_t gpionum)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
int group_pad_num = 0;
|
|
|
|
int gpio_group = 0;
|
|
|
|
u32 gpio_pad = 0;
|
|
|
|
|
|
|
|
group_pad_num = (gpionum % MAX_GPIO_PIN_PER_GROUP);
|
|
|
|
gpio_group = (gpionum / MAX_GPIO_PIN_PER_GROUP);
|
|
|
|
|
|
|
|
switch (gpio_group) {
|
|
|
|
case GPIO_LP_GROUP_A:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_A;
|
|
|
|
break;
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
case GPIO_LP_GROUP_B:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_B;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPIO_LP_GROUP_C:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_C;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPIO_LP_GROUP_D:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_D;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPIO_LP_GROUP_E:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_E;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPIO_LP_GROUP_F:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_F;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPIO_LP_GROUP_G:
|
|
|
|
gpio_pad = GPIO_LP_GROUP_GPP_G;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
break;
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
2015-05-13 03:23:27 +02:00
|
|
|
gpio_pad = (gpio_pad << GPIO_GROUP_SHIFT) + group_pad_num;
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
return gpio_pad;
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
int gpio_get(gpio_t gpio_num)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
u32 gpiopad = 0;
|
|
|
|
u32 outputvalue = 0;
|
|
|
|
int status = 0;
|
|
|
|
|
2015-05-13 03:19:47 +02:00
|
|
|
if (gpio_num > MAX_GPIO_NUMBER)
|
|
|
|
return 0;
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
gpiopad = convert_gpio_num_to_pad(gpio_num);
|
|
|
|
if (gpiopad < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
status = read_write_gpio_pad_reg(gpiopad,
|
|
|
|
0,
|
|
|
|
B_PCH_GPIO_TX_STATE,
|
|
|
|
READ, &outputvalue);
|
|
|
|
outputvalue >>= N_PCH_GPIO_TX_STATE;
|
|
|
|
return outputvalue;
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
void gpio_set(gpio_t gpio_num, int value)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
int status = 0;
|
|
|
|
u32 gpiopad = 0;
|
|
|
|
u32 outputvalue = 0;
|
|
|
|
|
|
|
|
if (gpio_num > MAX_GPIO_NUMBER)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gpiopad = convert_gpio_num_to_pad(gpio_num);
|
|
|
|
if (gpiopad < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
outputvalue = value;
|
|
|
|
|
|
|
|
status = read_write_gpio_pad_reg(gpiopad,
|
|
|
|
0,
|
|
|
|
B_PCH_GPIO_TX_STATE,
|
|
|
|
WRITE, &outputvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_all_smi(void)
|
|
|
|
{
|
|
|
|
u32 gpiogroupinfolength;
|
|
|
|
u32 gpioindex = 0;
|
|
|
|
|
|
|
|
gpiogroupinfolength = sizeof(gpio_group_info) / sizeof(GPIO_GROUP_INFO);
|
|
|
|
|
|
|
|
for (gpioindex = 0; gpioindex < gpiogroupinfolength; gpioindex++) {
|
|
|
|
/*Check if group has GPI SMI register */
|
|
|
|
if (gpio_group_info[gpioindex].smistsoffset ==
|
|
|
|
NO_REGISTER_PROPERTY)
|
|
|
|
continue;
|
|
|
|
/* Clear all GPI SMI Status bits by writing '1' */
|
|
|
|
pcr_write32(gpio_group_info[gpioindex].community,
|
|
|
|
gpio_group_info[gpioindex].smistsoffset,
|
|
|
|
0xFFFFFFFF);
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
void get_smi_status(u32 status[GPIO_COMMUNITY_MAX])
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
u32 num_of_communities;
|
|
|
|
u32 gpioindex;
|
|
|
|
u32 outputvalue = 0;
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
num_of_communities = ARRAY_SIZE(gpio_group_info);
|
2015-05-13 03:19:47 +02:00
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
for (gpioindex = 0; gpioindex < num_of_communities; gpioindex++) {
|
|
|
|
/*Check if group has GPI SMI register */
|
|
|
|
if (gpio_group_info[gpioindex].smistsoffset ==
|
|
|
|
NO_REGISTER_PROPERTY)
|
|
|
|
continue;
|
|
|
|
/* Read SMI status register */
|
|
|
|
pcr_read32(gpio_group_info[gpioindex].community,
|
|
|
|
gpio_group_info[gpioindex].smistsoffset,
|
|
|
|
&outputvalue);
|
|
|
|
status[gpioindex] = outputvalue;
|
|
|
|
}
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 03:23:27 +02:00
|
|
|
void enable_all_smi(void)
|
2015-05-13 03:19:47 +02:00
|
|
|
{
|
2015-05-13 03:23:27 +02:00
|
|
|
u32 gpiogroupinfolength;
|
|
|
|
u32 gpioindex = 0;
|
|
|
|
|
|
|
|
gpiogroupinfolength = sizeof(gpio_group_info) / sizeof(GPIO_GROUP_INFO);
|
|
|
|
|
|
|
|
for (gpioindex = 0; gpioindex < gpiogroupinfolength; gpioindex++) {
|
|
|
|
/*Check if group has GPI SMI register */
|
|
|
|
if (gpio_group_info[gpioindex].smienoffset ==
|
|
|
|
NO_REGISTER_PROPERTY)
|
|
|
|
continue;
|
|
|
|
/* Set all GPI SMI Enable bits by writing '1' */
|
|
|
|
pcr_write32(gpio_group_info[gpioindex].community,
|
|
|
|
gpio_group_info[gpioindex].smienoffset,
|
|
|
|
0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void enable_gpio_groupsmi(gpio_t gpio_num, u32 mask)
|
|
|
|
{
|
|
|
|
u32 gpioindex = 0;
|
|
|
|
u32 smien = 0;
|
|
|
|
|
|
|
|
if (gpio_num > MAX_GPIO_NUMBER)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gpioindex = (gpio_num / MAX_GPIO_PIN_PER_GROUP);
|
|
|
|
|
|
|
|
pcr_read32(gpio_group_info[gpioindex].community,
|
|
|
|
gpio_group_info[gpioindex].smienoffset, &smien);
|
|
|
|
smien |= mask;
|
|
|
|
/* Set all GPI SMI Enable bits by writing '1' */
|
|
|
|
pcr_write32(gpio_group_info[gpioindex].community,
|
|
|
|
gpio_group_info[gpioindex].smienoffset, smien);
|
2015-05-13 03:19:47 +02:00
|
|
|
}
|